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 对象的创建与使用
Mar 09 Javascript
Javascript 兼容firefox的一些问题
May 21 Javascript
JavaScript DOM学习第一章 W3C DOM简介
Feb 19 Javascript
Node.js事件循环(Event Loop)和线程池详解
Jan 28 Javascript
学习javascript面向对象 javascript实现继承的方式
Jan 04 Javascript
Vue.js动态组件解析
Sep 09 Javascript
bootstrap Table服务端处理分页(后台是.net)
Oct 19 Javascript
玩转Koa之核心原理分析
Dec 29 Javascript
重学JS之显示强制类型转换详解
Jun 30 Javascript
layui 上传图片 返回图片地址的方法
Sep 26 Javascript
Vue使用vue-recoure + http-proxy-middleware + vuex配合promise实现基本的跨域请求封装
Oct 21 Javascript
JS绘图Flot如何实现可选显示曲线图功能
Oct 16 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类的特性实例分析
2016/09/28 PHP
通过javascript的匿名函数来分析几段简单有趣的代码
2010/06/29 Javascript
Js 冒泡事件阻止实现代码
2013/01/27 Javascript
JavaScript修改css样式style动态改变元素样式
2013/12/16 Javascript
jQuery的Scrollify插件实现滑动到页面下一节点
2015/07/05 Javascript
js立即执行函数: (function ( ){})( ) 与 (function ( ){}( )) 有什么区别?
2015/11/18 Javascript
简单实现jQuery进度条轮播实例代码
2016/06/20 Javascript
AngularJs Managing Service Dependencies详解
2016/09/02 Javascript
Angular2安装angular-cli
2017/05/21 Javascript
bootstrap switch开关组件使用方法详解
2017/08/22 Javascript
JS写XSS cookie stealer来窃取密码的步骤详解
2017/11/20 Javascript
详解vue-cli中模拟数据的两种方法
2018/07/03 Javascript
微信小程序12行js代码自己写个滑块功能(推荐)
2020/07/15 Javascript
python logging日志模块的详解
2017/10/29 Python
python调用百度语音REST API
2018/08/30 Python
python判断自身是否正在运行的方法
2019/08/08 Python
python函数声明和调用定义及原理详解
2019/12/02 Python
python实现回旋矩阵方式(旋转矩阵)
2019/12/04 Python
Python基于pygame实现单机版五子棋对战
2019/12/26 Python
python 基于selectors库实现文件上传与下载
2020/12/31 Python
python中time.ctime()实例用法
2021/02/03 Python
德国机场停车位比较和预订网站:Ich-parke-billiger
2018/01/08 全球购物
lululemon美国官网:瑜伽服+跑步装备
2018/11/16 全球购物
某公司的.net工程师面试题笔试题
2013/11/22 面试题
编程用JAVA解析XML的方式
2013/07/07 面试题
网络维护中文求职信
2014/01/03 职场文书
幼儿园秋游感想
2014/03/12 职场文书
交通安全责任书范本
2014/07/24 职场文书
法定代表人身份证明书(含说明)
2014/10/02 职场文书
2014年电信员工工作总结
2014/12/19 职场文书
2016国庆促销广告语
2016/01/28 职场文书
简历自我评价:教师师德表现自我评价
2019/04/24 职场文书
导游词之上海东方明珠塔
2019/09/25 职场文书
用基于python的appium爬取b站直播消费记录
2021/04/17 Python
java实现对Hadoop的操作
2021/07/01 Java/Android
Opencv实现二维直方图的计算及绘制
2021/07/21 Python