node.js下when.js 的异步编程实践


Posted in Javascript onDecember 03, 2014

假设一个业务场景:

通过rss地址,获取rss并保存于文件,rss地址保存于文件中。

完成该场景的业务需要完成3个任务:

1.从文件中读取rss地址。

2.获取rss。

3.保存于文件。

最后将这三个任务进行整合。

准备:

存放rss地址的文件,address.txt。

http://programmer.csdn.net/rss_programmer.html
 
任务1:

读取rss地址文件的内容并通过callback返回。

var getRssAddress = function(path, callback) {

  fs.readFile(path, {encoding: 'utf8'}, function (err, data) {

    callback(err, data);

  });

}

任务2:

 通过rss地址get到rss,并通过callback返回错误或数据。

var getRss = function(url, callback) {

  var data = '';

  http.get(url, function(res) {

    res.on('data', function(chrunk) {

      data += chrunk;

    });

    res.on('end', function() {

      callback(null, data);

    });

  }).on('error', function(err) {

    callback(err, null);

  });

}

 

任务3:

将rss保存于文件并通过callback返回错误。

var saveRss = function(data, callback) {

  fs.writeFile('rss.txt', data, 'utf8', function(err) {

    callback(err);

  });

}

整合:

getRssAddress('address.txt', function(err, data) {

  if(err) {

    console.log(err);

    return;

  }

  getRss(data, function(err, data) {

    if(err) {

      console.log(err);

      return;

    }

    saveRss(data, function(err) {

      if(err) console.log(err);

    });

  });

});

上面的代码是全异步处理,使用最常见的callback处理异步逻辑的返回,好处是标准写法,大家都能容易接受;坏处是耦合性太强,处理异常麻烦,代码不直观,特别是处理业务逻辑复杂和处理任务多的场景,层层的callback会让人眼冒金星,代码难以维护。

Promise/A规范的实现之一when.js正是针对这样的问题域。

让我们来看一下改造后的代码。

任务1:

var getRssAddress = function(path) {

    var deferred = when.defer();

      fs.readFile(path, {encoding: 'utf8'}, function (err, data) {

        if (err) deferred.reject(err);

        deferred.resolve(data);

      });
    return deferred.promise;

}

 
任务2:
var getRss = function(url) {

  var deferred = when.defer();

    var data = '';

    http.get(url, function(res) {

      res.on('data', function(chrunk) {

        data += chrunk;

      });

      res.on('end', function() {

        deferred.resolve(data);

      });

    }).on('error', function(err) {

      deferred.reject(err);

    });
    return deferred.promise;

}

任务3:

var saveRss = function(data) {

  var deferred = when.defer();

  fs.writeFile('rss.txt', data, 'utf8', function(err) {

    if(err) deferred.reject(err);

    deferred.resolve();

  });
  return deferred.promise;

}

 

整合:

getRssAddress('address.txt')

  .then(getRss)

  .then(saveRss)

  .catch(function(err) {

    console.log(err);

  });

解释:

promise/A规范定义的“Deferred/Promise”模型就是“发布/订阅者”模型,通过Deferred对象发布事件,可以是完成resolve事件,或者是失败reject事件;通过Promise对象进行对应完成或失败的订阅。

在Promises/A规范中,每个任务都有三种状态:默认(pending)、完成(fulfilled)、失败(rejected)。

1.默认状态可以单向转移到完成状态,这个过程叫resolve,对应的方法是deferred.resolve(promiseOrValue);

2.默认状态还可以单向转移到失败状态,这个过程叫reject,对应的方法是deferred.reject(reason);

3.默认状态时,还可以通过deferred.notify(update)来宣告任务执行信息,如执行进度;

4.状态的转移是一次性的,一旦任务由初始的pending转为其他状态,就会进入到下一个任务的执行过程中。

按照上面的代码。

通过when.defer定义一个deferred对象。

var deferred = when.defer();
异步数据获取成功后,发布一个完成事件。

deferred.resolve(data);
异步数据获取失败后,发布一个失败事件。

deferred.reject(err);
并且返回Promise对象作为订阅使用。

return deferred.promise;
订阅是通过Promise对象的then方法进行完成/失败/通知的订阅。

getRssAddress('address.txt')
  .then(getRss)
then有三个参数,分别是onFulfilled、onRejected、onProgress

promise.then(onFulfilled, onRejected, onProgress)
上一个任务被resolve(data),onFulfilled函数就会被触发,data作为它的参数.

上一个任务被reject(reason),那么onRejected就会被触发,收到reason。

任何时候,onFulfilled和onRejected都只有其一可以被触发,并且只触发一次。

对于处理异常,when.js也提供了极其方便的方法,then能传递错误,多个任务串行执行时,我们可以只在最后一个then定义onRejected。也可以在最后一个then的后面调用catch函数捕获任何一个任务的异常。

如此写法简单明了。

getRssAddress('address.txt')

  .then(getRss)

  .then(saveRss)

  .catch(function(err) {

    console.log(err);

  });

Promise给异步编程带来了巨大的方便,可以让我们专注于单个任务的实现而不会陷入金字塔厄运,以上代码仅仅是基本使用,when.js提供的功能远远不止本文提到的这些,具体参照官方API。

Javascript 相关文章推荐
jquery中选择块并改变属性值的方法
Jul 31 Javascript
顶部缓冲下拉菜单导航特效的JS代码
Aug 27 Javascript
JavaScript实现在页面间传值的方法
Apr 07 Javascript
jQuery结合CSS制作漂亮的select下拉菜单
May 03 Javascript
基于javascript实现图片左右切换效果
Jan 25 Javascript
ReactNative-JS 调用原生方法实例代码
Oct 08 Javascript
Vuex和前端缓存的整合策略详解
May 09 Javascript
移动端效果之IndexList详解
Oct 20 Javascript
angularJS实现动态添加,删除div方法
Feb 27 Javascript
一次Webpack配置文件的分离实战记录
Nov 30 Javascript
layui实现下拉框三级联动
Jul 26 Javascript
Vue实现手机扫描二维码预览页面效果
May 28 Javascript
jquery操作 iframe的方法
Dec 03 #Javascript
使用js实现数据格式化
Dec 03 #Javascript
使用js获取图片原始尺寸
Dec 03 #Javascript
上传文件返回的json数据会被提示下载问题解决方案
Dec 03 #Javascript
使用jQuery实现验证上传图片的格式与大小
Dec 03 #Javascript
使用正则表达式的格式化与高亮显示json字符串
Dec 03 #Javascript
jquery中获取元素里某一特定子元素的代码
Dec 02 #Javascript
You might like
世界咖啡生产者论坛呼吁:需要立即就咖啡价格采取认真行动
2021/03/06 咖啡文化
php park、unpark、ord 函数使用方法(二进制流接口应用实例)
2010/10/19 PHP
JS与PHP向函数传递可变参数的区别实例代码
2011/05/18 PHP
最新的10款jQuery内容滑块插件分享
2011/09/18 Javascript
jquery插件珍藏(图片局部放大/信息提示框)
2013/01/08 Javascript
js取值中form.all和不加all的区别介绍
2014/01/20 Javascript
innerHTML在IE中报错解决方案
2014/12/15 Javascript
jQuery oLoader实现的加载图片和页面效果
2015/03/14 Javascript
JavaScript事件 "事件对象"的注意要点
2016/01/14 Javascript
jQuery遍历节点树方法分析
2016/09/08 Javascript
Node.js 多进程处理CPU密集任务的实现
2019/05/26 Javascript
Vue+element+cookie记住密码功能的简单实现方法
2020/09/20 Javascript
[46:14]完美世界DOTA2联赛PWL S3 Magma vs INK ICE 第一场 12.11
2020/12/16 DOTA
python的id()函数解密过程
2012/12/25 Python
Python读写Excel文件的实例
2013/11/01 Python
用yum安装MySQLdb模块的步骤方法
2016/12/15 Python
Python编程实现二叉树及七种遍历方法详解
2017/06/02 Python
python+selenium实现QQ邮箱自动发送功能
2019/01/23 Python
使用Python在Windows下获取USB PID&VID的方法
2019/07/02 Python
Django项目之Elasticsearch搜索引擎的实例
2019/08/21 Python
Python性能测试工具Locust安装及使用
2020/12/01 Python
苹果台湾官网:Apple台湾
2019/01/05 全球购物
Herschel美国官网:背包、手提袋及配件
2020/03/10 全球购物
法国在线药房:1001Pharmacies
2021/03/07 全球购物
数据库方面面试题
2012/04/22 面试题
管理科学大学生求职信
2013/11/13 职场文书
银行柜员求职自荐书
2014/06/18 职场文书
求职自我评价范文100字
2014/09/23 职场文书
高校教师个人工作总结2014
2014/12/17 职场文书
小学语文复习计划
2015/01/19 职场文书
世界气象日活动总结
2015/02/27 职场文书
简单的辞职信怎么写
2015/02/28 职场文书
增值税发票丢失证明
2015/06/19 职场文书
Maven学习----Maven安装与环境变量配置教程
2021/06/29 Java/Android
MySQL基础快速入门知识总结(附思维导图)
2021/09/25 MySQL
Java 垃圾回收超详细讲解记忆集和卡表
2022/04/08 Java/Android