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 30 Javascript
浅谈javascript的数据类型检测
Jul 10 Javascript
jquery异步调用页面后台方法‏(asp.net)
Mar 01 Javascript
基于jquery的仿百度搜索框效果代码
Apr 11 Javascript
javascript和HTML5利用canvas构建猜牌游戏实现算法
Jul 17 Javascript
原生js编写设为首页兼容ie、火狐和谷歌
Jun 05 Javascript
JS实现网页背景颜色与select框中颜色同时变化的方法
Feb 27 Javascript
jQuery实现鼠标经过提示信息的地图热点效果
Apr 26 Javascript
JS两种类型的表单提交方法实例分析
Nov 28 Javascript
如何提高Dom访问速度
Jan 05 Javascript
详解如何使用微信小程序云函数发送短信验证码
Mar 13 Javascript
vue表单数据交互提交演示教程
Nov 13 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
打造计数器DIY三步曲(下)
2006/10/09 PHP
php截取utf-8中文字符串乱码的解决方法
2010/03/29 PHP
微信公众平台消息接口校验与消息接口响应实例
2014/12/23 PHP
php编写的抽奖程序中奖概率算法
2015/05/14 PHP
Yii2.0建立公共方法简单示例
2019/01/29 PHP
两个JavaScript jsFiddle JSBin在线调试器
2010/03/14 Javascript
可以将word转成html的js代码
2010/04/11 Javascript
将form表单中的元素转换成对象的方法适用表单提交
2014/05/02 Javascript
一段非常简单的js判断浏览器的内核
2014/08/17 Javascript
jQuery动画显示和隐藏效果实例演示(附demo源码下载)
2015/12/31 Javascript
Node.js重新刷新session过期时间的方法
2016/02/04 Javascript
JS中作用域和变量提升(hoisting)的深入理解
2016/10/31 Javascript
JS实现的加减乘除四则运算计算器示例
2017/08/09 Javascript
Nodejs把接收图片base64格式保存为文件存储到服务器上
2018/09/26 NodeJs
JS+css3实现幻灯片轮播图
2020/08/14 Javascript
[01:33]完美世界DOTA2联赛PWL S3 集锦第二期
2020/12/21 DOTA
用PyQt进行Python图形界面的程序的开发的入门指引
2015/04/14 Python
Django框架下在视图中使用模版的方法
2015/07/16 Python
Python 创建新文件时避免覆盖已有的同名文件的解决方法
2018/11/16 Python
在python中使用with打开多个文件的方法
2019/01/07 Python
python cumsum函数的具体使用
2019/07/29 Python
使用pyecharts1.7进行简单的可视化大全
2020/05/17 Python
大学生职业生涯规划范文
2013/12/31 职场文书
小学数学教学反思
2014/02/02 职场文书
汇源肾宝广告词
2014/03/20 职场文书
二年级小学生评语
2014/04/21 职场文书
领导干部贪图享乐整改措施
2014/09/21 职场文书
正规借条模板
2015/05/26 职场文书
经营场所使用证明
2015/06/19 职场文书
2015大学生入党个人自传
2015/06/26 职场文书
2016年国培研修日志
2015/11/13 职场文书
2016年小学生迎国庆广播稿
2015/12/18 职场文书
2016教师廉洁从教心得体会
2016/01/13 职场文书
2016年春季趣味运动会开幕词
2016/03/04 职场文书
浅谈MySQL 亿级数据分页的优化
2021/06/15 MySQL
Java死锁的排查
2022/05/11 Java/Android