NodeJS中利用Promise来封装异步函数

 在写Node.js的过程中,连续的IO操作可能会导致“金字塔噩梦”,回调函数的多重嵌套让代码变的难以维护,利用CommonJs的Promise来封装异步函数,使用统一的链式API来摆脱多重回调的噩梦。

  Node.js提供的非阻塞IO模型允许我们利用回调函数的方式处理IO操作,但是当需要连续的IO操作时,你的回调函数会多重嵌套,代码很不美观,而且不易维护,而且可能会有许多错误处理的重复代码,也就是所谓的“Pyramid of Doom”。

复制代码 代码如下:

step1(function (value1) {
    step2(value1, function(value2) {
        step3(value2, function(value3) {
            step4(value3, function(value4) {
                // Do something with value4
            });
        });
    });
});

  这其实就是Node.js的Control flow的问题,对于这个问题,解决方案都许多,比如利用async,或者eventProxy等,不过本文的主题是利用CommonJs规范中对Promise来解决这个问题。

什么是Promise?

  CommonJs的Promise规范有许多种,我们一般讨论的是Promise/A+规范,它定义了Promise的基本行为。

  Promise是一个对象,它通常代表一个在未来可能完成的异步操作。这个操作可能成功也可能失败,所以一个Promise对象一般有3个状态:Pending,Fulfilled,Rejected。分别代表未完成、成功完成和操作失败。一旦Promise对象的状态从Pending变成Fulfilled或者Rejected任意一个,它的状态都没有办法再被改变。

  一个Promise对象通常会有一个then方法,这个方法让我们可以去操作未来可能成功后返回的值或者是失败的原因。这个then方法是这样子的:

promise.then(onFulfilled, onRejected)
  显而易见的是,then方法接受两个参数,它们通常是两个函数,一个是用来处理操作成功后的结果的,另一个是用来处理操作失败后的原因的,这两个函数的第一个参数分别是成功后的结果和失败的原因。如果传给then方法的不是一个函数,那么这个参数会被忽略。

  then方法的返回值是一个Promise对象,这一个特点允许我们链式调用then来达到控制流程的效果。这里有许多细节上的问题,比如值的传递或者错误处理等。Promise的规范是这样定义的:

onFulfilled或者onRejected函数的返回值不是Promise对象,则该值将会作为下一个then方法中onFulfilled的第一个参数,如果返回值是一个Promise对象,怎么then方法的返回值就是该Promise对象
onFulfilled或者onRejected函数中如果有异常抛出,则该then方法的返回的Promise对象状态转为Rejected,如果该Promise对象调用then,则Error对象会作为onRejected函数的第一个参数
如果Promise状态变为Fulfilled而在then方法中没有提供onFulfilled函数,则then方法返回的Promise对象状态变为Fulfilled且成功的结果为上一个Promise的结果,Rejected同理。
  补充一句,onFulfilled和onRejected都是异步执行的。

规范的实现:q

  上面讲的是Promise的规范,而我们需要的是它的实现,q是一个对Promise/A+有着较好实现规范的库。

  首先我们需要创建一个Promise对象,关于Promise对象创建的规范在Promise/B中,这里不做详细的解释,直接上代码。

复制代码 代码如下:

    function(flag){
        var defer = q.defer();
        fs.readFile("a.txt", function(err, data){
        if(err) defer.reject(err);
            else defer.resolve(data);
            });
            return defer.promise;
    }

  多数Promise的实现在Promise的创建上大同小异,通过创建一个具有promise属性的defer对象,如果成功获取到值则调用defer.resolve(value),如果失败,则调用defer.reject(reason),最后返回defer的promise属性即可。这个过程可以理解为调用defer.resolve将Promise的状态变成Fulfilled,调用defer.reject将Promise的状态变成Rejected。

  在面对一系列连续的异步方法时,怎么利用Promise写出漂亮的代码呢?看下下面的例子。

复制代码 代码如下:

    promise0.then(function(result){
        // dosomething
        return result;
    }).then(function(result) {
        // dosomething
        return promise1;   
    }).then(function(result) {
        // dosomething
    }).catch(function(ex) {
        console.log(ex);
    }).finally(function(){
        console.log("final");
    });

  在上面的代码中,then方法只接受OnFulfilled,而catch方法实际上就是then(null, OnRejected),这样的话只要一系列异步方法只要始终是成功返回值的,那么代码就会瀑布式的向下运行,如果其中任意一个异步方法失败或者发生异常,那么根据CommonJs的Promise规范,将执行catch中的function。q还提供了finally方法,从字面上也很好理解,就是不论resolve还是reject,最终都会执行finally中的function。

  看上去似乎不错,代码更以维护且美观了,那么如果希望并发呢?

复制代码 代码如下:

     q.all([promise0, promise1, promise2]).spread(function(val0, val1, val2){
                    console.log(arguments);
                }).then(function(){
                    console.log("done");
                }).catch(function(err){
                    console.log(err);
                });

  q也为并发提供了api,调用all方法并传递一个Promise数组即可继续使用then的链式风格。还有像q.nfbind等可以将Node.js的原生API转化成Promise来统一代码格式也是挺好的。更多api在这里就不一一详述了。

结论

  本文主要介绍通过使用Promise来解决Node.js控制流问题,但Promise也可同样应用于前端,EMCAScript6已经提供了原生的API支持。需要指出的是Promise并不是唯一的解决方案,async也是一个很好的选择,并且提供更友好的并发控制API,不过我觉得Promise在封装具有异步方法的函数时更具优势。

好了,本文就先到这里了,希望对大家能够有所帮助。

本页内容版权归属为原作者,如有侵犯您的权益,请通知我们删除。
今天看到有百度验证码挺有意思的,就把它仿下来了。 利用JS实现当选择满4个后自动对比,本文中采用静态对比的方法看你是否全部选择对。然后提示【答案正确!】或【答案错误!】, 以后有空再去做成程序,先分享给大家! 效果图如下: 源代码如下: 复制代码 代码如下: !DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd" html
复制代码 代码如下: %@ page contentType="text/html; charset=utf-8"% !DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd" html xmlns="http://www.w3.org/1999/xhtml" head meta http-equiv="Content-Type
ignoreCase是正则表达式对象的只读布尔属性。它指定是否一个特定的正则表达式执行不区分大小写的匹配。,它与“i”属性创建。 语法 RegExpObject.ignoreCase 下面是参数的详细信息: NA 返回值: 如果“i”修改被设置返回“TRUE”,否则返回“FALSE”。 例子: htmlheadtitleJavaScript RegExp ignoreCase Property/title/headbodyscript type="text/javascript" var re = new
本文实例讲述了JQuery分屏指示器图片轮换效果实现方法。分享给大家供大家参考。具体分析如下: 在Web App大行其道的今天,分屏指示器用得非常广泛,从Android、到腾讯的Web OS等等。分屏指示器给人很好的用户体验,下面就实现一个分屏指示器,用于实现图片的简单轮换效果,仅抛砖引玉~ 代码如下: script type="text/javascript" var curr = 0, next = 0, count = 0; $(document).ready(function () { // 记录
当打开一个新窗口时,有些用户想直接最大化显示,不想再多一步操作。如果不在代码中做出明确设置的话,这个一般来说是浏览器自己的设置问题。但用户希望无论什么情况,无论浏览器处于什么样的设置,都要让这个弹出的窗口最大化,那么怎么办呢?其实很好办,只需要把几句代码,让浏览器窗口从默认大小变成我们自己的设置。 复制代码 代码如下: $(function(){ if (window.screen) { var myw = screen.availWidth; var myh = screen.availHeight; w
本文实例讲述了javascript基于DOM实现省市级联下拉框的方法。分享给大家供大家参考。具体实现方法如下: html xmlns="http://www.w3.org/1999/xhtml"headmeta http-equiv="Content-Type" content="text/html; charset=utf-8" /title省市级联下拉框/titlescript type="text/javascript"var provs = { "江西省": ["南昌市", "景德镇", "九江"
DOM树 首先,可视化一个HMTL文档的DOM树是很有帮助的。一个简单的HTML页面看起来就像是这个样子: 事件冒泡(又称事件传播) 当我们点击一个链接时,其触发了链接元素的单击事件,该事件则引发任何我们已绑定到该元素的单击事件上的函数的执行。 复制代码 代码如下: $('a').bind('click',function(){alert('that tickles!')}) 因此一个单击操作会触发alert函数的执行。 click事件接着会向树的根方向传播,广播到父元素,然后接着是每个祖先元素,只要是它
本文实例讲述了JavaScript实现随机替换图片的方法。分享给大家供大家参考。具体如下: 下面代码可实现将页面上的图片随机替换成别的图片,或者随机显示一个图片 scriptdocument.write(img src="img/+parseInt(Math.random()*(5))+.gif"height="40" width="50"/script 其中的图片文件名为: 0.gif 1.gif 2.gif 3.gif 4.gif 希望本文所述对大家的javascript程序设计有所帮助。
本文实例讲述了javaScript中this用法。分享给大家供大家参考。具体分析如下: 之前学javascript的时候总搞不清this,这个this不像java里的this那么好理解。我后来也是看了许多别人写的文章,才理解过来的。现在把别人写的东西搬过来,怕以后忘记。 总得来说 this的指向分三种。指向全局window,该对象,构造函数。 结论: 在Javascript中,this指向函数执行时的当前对象。 简单点说就是调用的方法属于哪个对象,this就指向那个对象。 1. 全局window 简单代码
闲来无事,搞了个页面的分屏效果,先来看下效果: 出于可自定义宽高的目的,屏幕分块由CSS控制,由js控制估计等分模块效果一般. 程序相关说明: HTML结构: div class="header" header/div div class="container" id="displayArea" !-- 分屏内容渲染区域 -- /divdiv class="footer" !--省略其他代码--/div js调用: //分屏数据 var iframes = [ { id:'frames_1', frame