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 相关文章推荐
基于jquery的当鼠标滚轮到最底端继续加载新数据思路分享(多用于微博、空间、论坛 )
Oct 10 Javascript
JavaScript中的prototype.bind()方法介绍
Apr 04 Javascript
js判断元素是否隐藏的方法
Jun 09 Javascript
JavaScript常用基础知识强化学习
Dec 09 Javascript
JavaScript文档碎片操作实例分析
Dec 12 Javascript
通过V8源码看一个关于JS数组排序的诡异问题
Aug 14 Javascript
Vuex 入门教程
Jan 10 Javascript
在vue中获取token,并将token写进header的方法
Sep 26 Javascript
详解vue-router导航守卫
Jan 19 Javascript
webpack4手动搭建Vue开发环境实现todoList项目的方法
May 16 Javascript
tracking.js实现前端人脸识别功能
Apr 16 Javascript
JavaScript如何使用插值实现图像渐变
Jun 28 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面向对象全攻略 (十一)__toString()用法 克隆对象 __call处理调用错误
2009/09/30 PHP
php中过滤非法字符的具体实现
2013/10/29 PHP
php共享内存段示例分享
2014/01/20 PHP
win10环境PHP 7 安装配置【教程】
2016/05/09 PHP
PHP API接口必备之输出json格式数据示例代码
2017/06/27 PHP
通过PHP的Wrapper无缝迁移原有项目到新服务的实现方法
2020/04/02 PHP
Ext对基本类型的扩展 ext,extjs,format
2010/12/25 Javascript
理解javascript中的回调函数(callback)
2014/09/02 Javascript
javascript常用代码段搜集
2014/12/04 Javascript
理解javascript封装
2016/02/23 Javascript
JavaScript实现简单Tip提示框效果
2016/04/20 Javascript
ros::spin() 和 ros::spinOnce()函数的区别及详解
2016/10/01 Javascript
JavaScript实现垂直向上无缝滚动特效代码
2016/11/23 Javascript
jquery的父、子、兄弟节点查找,节点的子节点循环方法
2016/12/07 Javascript
浅谈JavaScript的innerWidth与innerHeight
2017/10/12 Javascript
微信小程序switch组件使用详解
2018/01/31 Javascript
Vue项目分环境打包的实现步骤
2018/04/02 Javascript
手挽手带你学React之React-router4.x的使用
2019/02/14 Javascript
[50:15]VP vs Mineski 2018国际邀请赛淘汰赛BO3 第二场 8.22
2018/08/23 DOTA
python创建和使用字典实例详解
2013/11/01 Python
打包发布Python模块的方法详解
2016/09/18 Python
python3.6.3+opencv3.3.0实现动态人脸捕获
2018/05/25 Python
Python-ElasticSearch搜索查询的讲解
2019/02/25 Python
pandas删除行删除列增加行增加列的实现
2019/07/06 Python
Python PIL图片添加字体的例子
2019/08/22 Python
使用python+whoosh实现全文检索
2019/12/09 Python
Python 实现数组相减示例
2019/12/27 Python
Python如何创建装饰器时保留函数元信息
2020/08/07 Python
python实现简单的tcp 文件下载
2020/09/16 Python
html5表单及新增的改良元素详解
2016/06/07 HTML / CSS
德国柯吉澳趣味家居:Koziol
2017/08/24 全球购物
土耳其时尚潮流在线购物网站:Trendyol
2017/10/10 全球购物
文明学生标兵事迹
2014/01/21 职场文书
妇联领导班子剖析材料
2014/08/21 职场文书
2019交通安全宣传标语集锦!
2019/06/28 职场文书
《文化苦旅》读后感:阅读,让人诗意地栖居在大地上
2019/12/24 职场文书