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 相关文章推荐
js 鼠标点击事件及其它捕获
Jun 04 Javascript
根据一段代码浅谈Javascript闭包
Dec 14 Javascript
js精度溢出解决方案
Dec 02 Javascript
使用jQuery和Bootstrap实现多层、自适应模态窗口
Dec 22 Javascript
JS延时器提示框的应用实例代码解析
Apr 27 Javascript
jQuery中弹出iframe内嵌页面元素到父页面并全屏化的实例代码
Dec 27 Javascript
原生js实现无缝轮播图效果
Jan 11 Javascript
JS获取短信验证码倒计时的实现代码
May 22 Javascript
JS库particles.js创建超炫背景粒子插件(附源码下载)
Sep 13 Javascript
Node.js学习教程之HTTP/2服务器推送【译】
Oct 31 Javascript
jsonp跨域获取百度联想词的方法分析
May 13 Javascript
vue+vant 上传图片需要注意的地方
Jan 03 Vue.js
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下实现折线图效果的代码
2007/04/28 PHP
PHP面向对象分析设计的经验原则
2008/09/20 PHP
PHP读取ACCESS数据到MYSQL的代码
2011/05/11 PHP
PHP json_encode中文乱码问题的解决办法
2013/09/09 PHP
php二维码生成以及下载实现
2017/09/28 PHP
JavaScript高级程序设计 扩展--关于动态原型
2010/11/09 Javascript
javascript将相对路径转绝对路径示例
2014/03/14 Javascript
jquery+ajax实现跨域请求的方法
2015/01/20 Javascript
jquery实现多条件筛选特效代码分享
2015/08/28 Javascript
JS+CSS简单树形菜单实现方法
2015/09/12 Javascript
JS实现可自定义大小,可双击关闭的弹出层效果
2015/10/16 Javascript
表单验证正则表达式实例代码详解
2015/11/09 Javascript
jQuery中的siblings用法实例分析
2015/12/24 Javascript
Node.js通过身份证号验证年龄、出生日期与性别方法示例
2017/03/09 Javascript
Angular移动端页面input无法输入的解决方法
2017/11/14 Javascript
vue项目引入ts步骤(小结)
2019/10/31 Javascript
VuePress 中如何增加用户登录功能
2019/11/29 Javascript
js实现无刷新监听URL的变化示例代码详解
2020/06/03 Javascript
详解实现vue的数据响应式原理
2021/01/20 Vue.js
[06:16]第十四期-国士无双绝地翻盘之撼地神牛
2014/06/24 DOTA
在Windows8上的搭建Python和Django环境
2014/07/03 Python
Python实现从url中提取域名的几种方法
2014/09/26 Python
Python排序算法之选择排序定义与用法示例
2018/04/29 Python
Python调用服务接口的实例
2019/01/03 Python
利用Python代码实现一键抠背景功能
2019/12/29 Python
python filecmp.dircmp实现递归比对两个目录的方法
2020/05/22 Python
使用css3制作登录表单的步骤
2014/04/07 HTML / CSS
HTML5 File接口在web页面上使用文件下载
2017/02/27 HTML / CSS
英国袜子店:Sock Shop
2017/01/11 全球购物
新年寄语大全
2014/04/12 职场文书
三方协议书范本
2014/04/22 职场文书
社区清明节活动总结
2014/07/04 职场文书
驾驶员安全责任书范本
2014/07/24 职场文书
处罚决定书范文
2015/06/24 职场文书
jupyter notebook保存文件默认路径更改方法汇总(亲测可以)
2021/06/09 Python
忘记Grafana不要紧2种Grafana重置admin密码方法详细步骤
2022/04/07 Servers