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 相关文章推荐
用Greasemonkey 脚本收藏网站会员信息到本地
Oct 26 Javascript
js RuntimeObject() 获取ie里面自定义函数或者属性的集合
Nov 23 Javascript
js解析与序列化json数据(三)json的解析探讨
Feb 01 Javascript
jquery实现聚光灯效果的方法
Feb 06 Javascript
js老生常谈之this,constructor ,prototype全面解析
Apr 05 Javascript
jQuery文字提示与图片提示效果实现方法
Jul 04 Javascript
第一次接触神奇的Bootstrap表单
Jul 27 Javascript
JavaScript编写一个简易购物车功能
Sep 17 Javascript
assert()函数用法总结(推荐)
Jan 25 Javascript
jquery中关于bind()方法的使用技巧分享
Mar 30 jQuery
微信小程序实现登录页云层漂浮的动画效果
May 05 Javascript
Nuxt.js 静态资源和打包的操作
Nov 06 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
检测png图片是否完整的php代码
2010/09/06 PHP
PHP以指定字段为索引返回数据库所取的数据数组
2013/06/30 PHP
PHP变量的定义、可变变量、变量引用、销毁方法
2013/12/20 PHP
CMS中PHP判断系统是否已经安装的方法示例
2014/07/26 PHP
FleaPHP框架数据库查询条件($conditions)写法总结
2016/03/19 PHP
PHP开发之归档格式phar文件概念与用法详解【创建,使用,解包还原提取】
2017/11/17 PHP
php7函数,声明,返回值等新特性介绍
2018/05/25 PHP
解决laravel-admin 自己新建页面里 js 需要刷新一次的问题
2019/10/03 PHP
Jquery判断IE6等浏览器的代码
2011/04/05 Javascript
JavaScript设计模式之单例模式实例
2014/09/24 Javascript
Javascript BOM学习小结(六)
2015/11/26 Javascript
利用Javascript实现BMI计算器
2016/08/16 Javascript
bootstrap datetimepicker实现秒钟选择下拉框
2017/01/05 Javascript
Bootstrap模态框使用详解
2017/02/15 Javascript
基于vue 动态加载图片src的解决方法
2018/02/05 Javascript
移动端 Vue+Vant 的Uploader 实现上传、压缩、旋转图片功能
2019/06/10 Javascript
Python实现矩阵转置的方法分析
2017/11/24 Python
python通过elixir包操作mysql数据库实例代码
2018/01/31 Python
python selenium 对浏览器标签页进行关闭和切换的方法
2018/05/21 Python
Python中时间datetime的处理与转换用法总结
2019/02/18 Python
在Tensorflow中实现梯度下降法更新参数值
2020/01/23 Python
解决pip安装的第三方包在PyCharm无法导入的问题
2020/10/15 Python
加拿大消费电子和手机购物网站:The Source
2017/01/28 全球购物
李维斯牛仔裤英国官方网站:Levi’s英国
2019/10/10 全球购物
某IT外企面试题-二分法求方程!看看大家的C++功底
2015/07/04 面试题
如何保障Web服务器安全
2014/05/05 面试题
公司人力资源的自我评价
2014/01/02 职场文书
旅游管理专业生自荐信范文
2014/01/02 职场文书
军校大学生个人的自我评价
2014/02/17 职场文书
运动会广播稿150字(9篇)
2014/09/20 职场文书
银行求职信范文怎么写
2015/03/20 职场文书
2015领导干部廉洁自律工作总结
2015/07/23 职场文书
运动会广播稿50字
2015/08/19 职场文书
小学数学继续教育研修日志
2015/11/13 职场文书
tensorflow中的梯度求解及梯度裁剪操作
2021/05/26 Python
springcloud整合seata
2022/05/20 Java/Android