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获取地址栏参数
Dec 22 Javascript
Zero Clipboard js+swf实现的复制功能使用方法
Mar 07 Javascript
jquery退出each循环的写法
Feb 26 Javascript
一个JavaScript获取元素当前高度的实例
Oct 29 Javascript
Javascript基础教程之定义和调用函数
Jan 18 Javascript
AngularJS入门教程之MVC架构实例分析
Nov 01 Javascript
JS如何设置iOS中微信浏览器的title
Nov 22 Javascript
Angularjs 实现动态添加控件功能
May 25 Javascript
layui扩展上传组件模拟进度条的方法
Sep 23 Javascript
微信小程序实现底部弹出模态框
Nov 18 Javascript
手写Spirit防抖函数underscore和节流函数lodash
Mar 22 Javascript
vue配置型表格基于el-table拓展之table-plus组件
Apr 12 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无限级分类实现评论及回复功能
2019/02/18 PHP
jquery $(document).ready() 与window.onload的区别
2009/12/28 Javascript
图像替换新技术 状态域方法
2010/01/28 Javascript
javascript innerHTML使用分析
2010/12/03 Javascript
jquery对表单操作2
2011/04/06 Javascript
jquery动态加载图片数据练习代码
2011/08/04 Javascript
jQuery动态添加删除select项(实现代码)
2013/09/03 Javascript
js编码、解码函数介绍及其使用示例
2013/09/05 Javascript
JS获取文本框,下拉框,单选框的值的简单实例
2014/02/26 Javascript
js的[defer]和[async]属性
2014/11/24 Javascript
jQuery中width()方法用法实例
2014/12/24 Javascript
Jquery数字上下滚动动态切换插件
2015/08/08 Javascript
jQuery联动日历的实例解析
2016/12/02 Javascript
JS得到当前时间的方法示例
2017/03/24 Javascript
使用AngularJS2中的指令实现按钮的切换效果
2017/03/27 Javascript
微信小程序 五星评分的实现实例
2017/08/04 Javascript
微信小程序下拉刷新界面的实现
2017/09/28 Javascript
JavaScript设计模式之工厂模式和抽象工厂模式定义与用法分析
2018/07/26 Javascript
详解Element-UI中上传的文件前端处理
2019/08/07 Javascript
vue.js+ElementUI实现进度条提示密码强度效果
2020/01/18 Javascript
javascript自定义加载loading效果
2020/09/15 Javascript
Django重装mysql后启动报错:No module named ‘MySQLdb’的解决方法
2018/04/22 Python
python 统计一个列表当中的每一个元素出现了多少次的方法
2018/11/14 Python
Python中 CSV格式清洗与转换的实例代码
2019/08/29 Python
python批量修改xml属性的实现方式
2020/03/05 Python
python中turtle库的简单使用教程
2020/11/11 Python
python搜索算法原理及实例讲解
2020/11/18 Python
Pandas数据分析的一些常用小技巧
2021/02/07 Python
详解python3 GUI刷屏器(附源码)
2021/02/18 Python
深深扎根运动世界的生活品牌:Tillys
2017/10/30 全球购物
初中数学教学反思
2014/01/16 职场文书
个人委托书
2014/07/31 职场文书
2015年建筑工程工作总结
2015/05/13 职场文书
感恩教育观后感
2015/06/17 职场文书
学习新党章心得体会2016
2016/01/15 职场文书
Python常用配置文件ini、json、yaml读写总结
2021/07/09 Python