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 相关文章推荐
JavaScript 提升运行速度之循环篇 译文
Aug 15 Javascript
40款非常棒的jQuery 插件和制作教程(系列二)
Nov 02 Javascript
基于javascript实现单选及多选的向右和向左移动实例
Jul 25 Javascript
Bootstrap编写一个同时适用于PC、平板、手机的登陆页面
Jun 30 Javascript
JS代码实现百度地图 画圆 删除标注
Oct 12 Javascript
ReactNative Image组件使用详解
Aug 07 Javascript
Angular表格神器ui-grid应用详解
Sep 29 Javascript
浅谈使用React.setState需要注意的三点
Dec 18 Javascript
Element Table的row-class-name无效与动态高亮显示选中行背景色
Nov 30 Javascript
jQuery实现的卷帘门滑入滑出效果【案例】
Feb 18 jQuery
Vue+axios+WebApi+NPOI导出Excel文件实例方法
Jun 05 Javascript
详解JSON.stringify()的5个秘密特性
May 26 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
PHP十六进制颜色随机生成器功能示例
2017/07/24 PHP
php设计模式之建造器模式分析【星际争霸游戏案例】
2020/01/23 PHP
javascript学习笔记(四) Number 数字类型
2012/06/19 Javascript
window.event.keyCode兼容IE和Firefox实现js代码
2013/05/30 Javascript
flash遮住div问题的正确解决方法
2014/02/27 Javascript
js用typeof方法判断undefined类型
2014/07/15 Javascript
JavaScript字符串对象fromCharCode方法入门实例(用于把Unicode值转换为字符串)
2014/10/17 Javascript
jQuery拖拽插件gridster使用指南
2015/04/21 Javascript
实例讲解javascript注册事件处理函数
2016/01/09 Javascript
原生js获取元素样式的简单方法
2016/08/06 Javascript
DOM中事件处理概览与原理的全面解析
2016/08/16 Javascript
浅谈Node.js:理解stream
2016/12/08 Javascript
PHP7新特性简述
2017/06/11 Javascript
浅谈webpack性能榨汁机(打包速度优化)
2019/01/09 Javascript
node全局变量__dirname与__filename的区别
2019/01/14 Javascript
Node.js web 应用如何封装到Docker容器中
2020/09/01 Javascript
JavaScript实现点击切换验证码及校验
2021/01/10 Javascript
Python 自动化表单提交实例代码
2017/06/08 Python
Python利用matplotlib.pyplot绘图时如何设置坐标轴刻度
2018/04/09 Python
如何利用Pyecharts可视化微信好友
2019/07/04 Python
python实现小世界网络生成
2019/11/21 Python
python3 求约数的实例
2019/12/05 Python
Python3 元组tuple入门基础
2020/02/09 Python
斯巴达比赛商店:Spartan Race
2019/01/08 全球购物
CHARLES & KEITH加拿大官网:新加坡时尚品牌
2020/03/26 全球购物
圣彼得堡鲜花配送:Semicvetic
2020/09/15 全球购物
什么是聚集索引和非聚集索引
2012/01/17 面试题
经验丰富大学生村干部自我鉴定
2014/01/22 职场文书
家长对孩子的感言
2014/03/10 职场文书
情侣之间的道歉短信
2015/05/12 职场文书
社区安置帮教工作总结2015
2015/05/20 职场文书
地道战观后感400字
2015/06/04 职场文书
2015入党自传格式范文
2015/06/26 职场文书
2015年幼儿教育工作总结
2015/07/24 职场文书
李清照的诗词赏析(20首)
2019/08/22 职场文书
一篇文章搞懂python混乱的切换操作与优雅的推导式
2021/08/23 Python