文档章节

利用Promise包装你的异步方法

LeoG0816
 LeoG0816
发布于 2014/11/20 22:25
字数 1430
阅读 338
收藏 1
点赞 0
评论 0

  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在封装具有异步方法的函数时更具优势。

© 著作权归作者所有

共有 人打赏支持
LeoG0816
粉丝 13
博文 7
码字总数 8927
作品 0
浦东
程序员
co 函数库的含义和用法

进入正文之前,先插播一条消息。 我七年前翻译的《软件随想录》再版了(京东链接)。这次是《Joel论软件》两卷同时再版,第一卷是新译本,第二卷是我翻译的。 本书的作者是著名程序员、Stack...

阮一峰 ⋅ 2015/05/06 ⋅ 0

Promise的前世今生和妙用技巧

浏览器事件模型和回调机制 JavaScript作为单线程运行于浏览器之中,这是每本JavaScript教科书中都会被提到的。同时出于对UI线程操作的安全性考虑,JavaScript和UI线程也处于同一个线程中。因...

zting科技 ⋅ 2017/01/09 ⋅ 0

js 异步 编程

回调地狱 无法捕获异常 输出为undefined Tips: 为什么异步代码回调函数中的异常无法被最外层的try/catch语句捕获? 异步调用一般分为两个阶段,提交请求和处理结果,这两个阶段之间有事件循环...

阿豪boy ⋅ 02/09 ⋅ 0

JavaScript Promise查缺补漏

原文链接 Promise简介 ECMAscript6原生提供了Promise对象,由浏览器直接支持,目前大多数浏览器都已经实现了,低版本浏览器可以使用es6-promise库来填平兼容性问题。Promise最大的好处是把执...

bestvist ⋅ 04/24 ⋅ 0

javascript promise 的调用链

[mdn] ref: 链式调用 因为then方法返回一个Promise,你可以轻易地链式调用then。但当返回值不是从 onFulfilled 或者 onRejected 所返回的 Promise 时,它会自动包装为 resolve 过的 Promis...

sengw ⋅ 2016/12/12 ⋅ 8

高级前端人员必会之 - Promise

编后吐槽:目前最用心的一篇,写的快花眼,很详细,耐心看必受益匪浅 JavaScript的执行环境是「单线程」的。所谓单线程,是指JS引擎中负责解释和执行JavaScript代码的线程只有一个,也就是一...

果汁凉茶丶 ⋅ 2017/11/16 ⋅ 0

学习JavaScript编程中的Promise使用

学习JavaScript编程中的Promise使用 也许很多朋友还没听说过JavaScript中的Promise,可以参考这几篇文章 JavaScript Promise启示录 http://www.csdn.net/article/2014-05-28/2819979-JavaScr...

开元中国2015 ⋅ 2015/07/28 ⋅ 0

ES6 async-await函数记录

含义 async 函数是什么?一句话,它就是 Generator 函数的语法糖。 async函数对 Generator 函数的改进,体现在以下四点: 1. 内置执行器 Generator 函数的执行必须靠执行器,所以才有了co模块...

前端大白 ⋅ 05/28 ⋅ 0

Node.js 异步异闻录

本文首发在个人博客:http://muyunyun.cn/posts/7b9fdc87/ 提到 Node.js, 我们脑海就会浮现异步、非阻塞、单线程等关键词,进一步我们还会想到 buffer、模块机制、事件循环、进程、V8、libuv...

牧云云 ⋅ 2017/09/22 ⋅ 0

在 Javascript 中用 Promise 进行异步编程

异步模式在web编程中变得越来越重要,对于web主流语言Javscript来说,这种模式实现起来不是很利索,为此,许多Javascript 库(比如 jQuery和Dojo)添加了一种称为promise的抽象(有时也称之为...

鉴客 ⋅ 2012/02/07 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

BS与CS的联系与区别【简】

C/S是Client/Server的缩写。服务器通常采用高性能的PC、工作站或小型机,并采用大型数据库系统,如Oracle、Sybase、InFORMix或 SQL Server。客户端需要安装专用的客户端软件。 B/S是Brower/...

anlve ⋅ 45分钟前 ⋅ 0

发生了什么?Linus 又发怒了?

在一个 Linux 内核 4.18-rc1 的 Pull Request 中,开发者 Andy Shevchenko 表示其在对设备属性框架进行更新时,移除了 union 别名,这引发了 Linus 的暴怒。 这一次 Linus Torvalds 发怒的原...

问题终结者 ⋅ 今天 ⋅ 0

在树莓派上搭建一个maven仓库

在树莓派上搭建一个maven仓库 20180618 lambo init 项目说明 家里有台树莓派性能太慢。想搭建一个maven私服, 使用nexus或者 jfrog-artifactory 运行的够呛。怎么办呢,手写一个吧.所在这个...

林小宝 ⋅ 今天 ⋅ 0

Spring发展历程总结

转自与 https://www.cnblogs.com/RunForLove/p/4641672.html 目前很多公司的架构,从Struts2迁移到了SpringMVC。你有想过为什么不使用Servlet+JSP来构建Java web项目,而是采用SpringMVC呢?...

onedotdot ⋅ 今天 ⋅ 0

Python模块/包/库安装(6种方法)

Python模块/包/库安装(6种方法) 冰颖机器人 2016-11-29 21:33:26 一、方法1: 单文件模块 直接把文件拷贝到 $python_dir/Lib 二、方法2: 多文件模块,带setup.py 下载模块包(压缩文件zip...

cswangyx ⋅ 今天 ⋅ 0

零基础学习大数据人工智能,学习路线篇!系统规划大数据之路?

大数据处理技术怎么学习呢?首先我们要学习Python语言和Linux操作系统,这两个是学习大数据的基础,学习的顺序不分前后。 Python:Python 的排名从去年开始就借助人工智能持续上升,现在它已经...

董黎明 ⋅ 今天 ⋅ 0

openJdk和sun jdk的区别

使用过LINUX的人都应该知道,在大多数LINUX发行版本里,内置或者通过软件源安装JDK的话,都是安装的OpenJDK, 那么到底什么是OpenJDK,它与SUN JDK有什么关系和区别呢? 历史上的原因是,Ope...

jason_kiss ⋅ 今天 ⋅ 0

梳理

Redux 是 JavaScript 状态容器,提供可预测化的状态管理。 它是JS的状态容器,是一种解决问题的方式,所以即可以用于 react 也可以用于 vue。 需要理解其思想及实现方式。 应用中所有的 stat...

分秒 ⋅ 今天 ⋅ 0

Java 后台判断是否为ajax请求

/** * 是否是Ajax请求 * @param request * @return */public static boolean isAjax(ServletRequest request){return "XMLHttpRequest".equalsIgnoreCase(((HttpServletReques......

JavaSon712 ⋅ 今天 ⋅ 0

Redis 单线程 为何却需要事务处理并发问题

Redis是单线程处理,也就是命令会顺序执行。那么为什么会存在并发问题呢? 个人理解是,虽然redis是单线程,但是可以同时有多个客户端访问,每个客户端会有 一个线程。客户端访问之间存在竞争...

码代码的小司机 ⋅ 今天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部