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 相关文章推荐
某页码显示的helper 少量调整,另附js版
Sep 12 Javascript
利用了jquery的ajax实现二级联互动菜单
Dec 02 Javascript
jquery实现人性化的有选择性禁用鼠标右键
Jun 30 Javascript
jQuery插件实现大图全屏图片相册
Mar 14 Javascript
关于Vue.js一些问题和思考学习笔记(2)
Dec 02 Javascript
JavaScript、C# URL编码、解码总结
Jan 21 Javascript
vue 组件 全局注册和局部注册的实现
Feb 28 Javascript
vue短信验证性能优化如何写入localstorage中
Apr 25 Javascript
Vue中CSS动画原理的实现
Feb 13 Javascript
一文了解vue-router之hash模式和history模式
May 31 Javascript
vue2配置scss的方法步骤
Jun 06 Javascript
vue 实现模糊检索并根据其他字符的首字母顺序排列
Sep 19 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
关于页面优化和伪静态
2009/10/11 PHP
php实现图片缩略图的方法
2016/03/29 PHP
php的api数据接口书写实例(推荐)
2016/09/22 PHP
PHP实现简单计算器小程序
2020/08/28 PHP
IE7中javascript操作CheckBox的checked=true不打勾的解决方法
2009/12/07 Javascript
一些javascript一些题目的解析
2010/12/25 Javascript
实用的JS正则表达式(手机号码/IP正则/邮编正则/电话等)
2013/01/11 Javascript
javascript面向对象包装类Class封装类库剖析
2013/01/24 Javascript
jquery获取被勾选的checked(选中)的那一行的3列和4列的值
2013/07/04 Javascript
javascript基于DOM实现省市级联下拉框的方法
2015/05/14 Javascript
JS函数定义方式的区别介绍
2016/03/22 Javascript
javaScript 事件绑定、事件冒泡、事件捕获和事件执行顺序整理总结
2016/10/10 Javascript
jquery 抽奖小程序实现代码
2016/10/12 Javascript
js+html制作简单验证码
2017/02/16 Javascript
vue.js内置组件之keep-alive组件使用
2018/07/10 Javascript
在webstorm开发微信小程序之使用阿里自定义字体图标的方法
2018/11/15 Javascript
JS使用new操作符创建对象的方法分析
2019/05/30 Javascript
JavaScript多种图形实现代码实例
2020/06/28 Javascript
[33:23]VG vs Pain 2018国际邀请赛小组赛BO2 第二场 8.18
2018/08/19 DOTA
Python实现给qq邮箱发送邮件的方法
2015/05/28 Python
Python实现完整的事务操作示例
2017/06/20 Python
python数据类型判断type与isinstance的区别实例解析
2017/10/31 Python
Python学习_几种存取xls/xlsx文件的方法总结
2018/05/03 Python
python中多层嵌套列表的拆分方法
2018/07/02 Python
python实现QQ批量登录功能
2019/06/19 Python
如何用Python做一个微信机器人自动拉群
2019/07/03 Python
python处理document文档保留原样式
2019/09/23 Python
Python selenium爬虫实现定时任务过程解析
2020/06/08 Python
详解css3 mask遮罩实现一些特效
2018/10/24 HTML / CSS
StubHub墨西哥:购买和出售您的门票
2016/09/17 全球购物
越南电子产品购物网站:FPT Shop
2017/12/02 全球购物
伦敦哈德森鞋:Hudson Shoes
2018/02/06 全球购物
罗马尼亚在线杂货店:Pilulka.ro
2019/09/28 全球购物
合作投资意向书
2014/04/01 职场文书
校运会口号
2014/06/18 职场文书
实施意见格式范本
2015/06/05 职场文书