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

  这篇文章主要介绍了NodeJS中利用Promise来封装异步函数,使用统一的链式API来摆脱多重回调的噩梦,非常的实用的小技能,希望小伙伴们能够喜欢

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

 

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

 

时间: 2025-01-01 02:55:14

NodeJS中利用Promise来封装异步函数的相关文章

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

 在写Node.js的过程中,连续的IO操作可能会导致"金字塔噩梦",回调函数的多重嵌套让代码变的难以维护,利用CommonJs的Promise来封装异步函数,使用统一的链式API来摆脱多重回调的噩梦. Node.js提供的非阻塞IO模型允许我们利用回调函数的方式处理IO操作,但是当需要连续的IO操作时,你的回调函数会多重嵌套,代码很不美观,而且不易维护,而且可能会有许多错误处理的重复代码,也就是所谓的"Pyramid of Doom". 复制代码 代码如下: st

Promise模式将JS回调函数和异步函数彻底分离

文章简介:情况再复杂点,如果一个操作要等到多个异步 ajax 请求的完成才能进行,就会出现回调函数嵌套的情况,如果需要嵌套好几层,那你就只能自求多福了. 网页的交互越来越复杂,JavaScript 的异步操作也随之越来越多.如常见的 ajax 请求,需要在请求完成时响应操作,请求通常是异步的,请求的过程中用户还能进行其他的操作,不会对页面进行阻塞,这种异步的交互效果对用户来说是挺有友好的.但是对于开发者来说,要大量处理这种操作,就很不友好了.异步请求完成的操作必须预先定义在回调函数中,等到请求完

JavaScript异步回调的Promise模式封装实例_javascript技巧

网页的交互越来越复杂,JavaScript 的异步操作也随之越来越多.如常见的 ajax 请求,需要在请求完成时响应操作,请求通常是异步的,请求的过程中用户还能进行其他的操作,不会对页面进行阻塞,这种异步的交互效果对用户来说是挺有友好的.但是对于开发者来说,要大量处理这种操作,就很不友好了.异步请求完成的操作必须预先定义在回调函数中,等到请求完成就必须调用这个函数.这种非线性的异步编程方式会让开发者很不适应,同时也带来了诸多的不便,增加了代码的耦合度和复杂性,代码的组织上也会很不优雅,大大降低了

Android 开发中利用异步来优化运行速度和性能

本文讲的是Android 开发中利用异步来优化运行速度和性能, 我们知道,在Android框架中提供了很多异步处理的工具类.然而,他们中大部分实现是通过提供单一的后台线程来处理任务队列的.如果我们需要更多的后台线程的时候该怎么办呢? 大家都知道Android的UI更新是在UI线程中进行的(也称之为主线程).所以如果我们在UI线程中编写耗时任务都可能会阻塞UI线程更新UI.为了避免这种情况我们可以使用 AsyncTask, IntentService和Threads.在之前我写的一篇文章介绍了An

nodejs中async和promise使用上有什么区别?

问题描述 nodejs中async和promise使用上有什么区别? nodejs中async和promise使用上有什么区别?~~~~~~~ 解决方案 async中的callback是指令式的异步 promise是函数式的异步 解决方案二: 支持一下支持一下~~~~~~~~ 解决方案三: Bluebird-NodeJs的Promise

ios-IOS中如何将开源库AFNetworking的GET函数中获取的数据,在函数体外利用呢?

问题描述 IOS中如何将开源库AFNetworking的GET函数中获取的数据,在函数体外利用呢? 代码如下,如何在GET函数外获取到在其内解析到的数据,从而加以利用呢? // // ViewController.m // 1 // // Created by on 16-2-2. // Copyright (c) 2016年 . All rights reserved. // #import "ViewController.h" #import "AFHTTPRequest

WPS表格中利用函数快速分类汇总复杂数据

在实际工作中,用户经常需要对WPS数据表中的明细数据进行汇总求和,如果用户希望对如图1的复杂数据明细表中所有小计项目进行快速汇总,可能不是一件容易的事情. 图1 用户数据明细表 下面将向用户介绍如何利用WPS表格的常用函数进行快速汇总. SUM函数快速汇总 WPS表格提供的SUM函数可以对用户选择的区域一次性添加公式,因此用户毋须分别对小计.合计和总计栏目手工输入,从而达到快速求和的目的. 选择数据区域 首先用户只需要将各个小计的区域逐步选定,区域选择的最终结果如图 2所示. 步骤1 先使用鼠标

C++中利用构造函数与无名对象简化运算符重载函数

在完整描述思想之前,我们先看一下如下的例子,这个例子中的加运算符重载是以非成员函数的方式出现的: //程序作者:管宁//站点:www.cndev-lab.com//所有稿件均有版权,如要转载,请务必著名出处和作者 #include <iostream>using namespace std; class Test{ public: Test(int a) { Test::a = a; } friend Test operator + (Test&,int); public: int a

php中利用explode函数分割字符串到数组

 这篇文章主要介绍了php中利用explode函数分割字符串到数组,需要的朋友可以参考下 分割字符串    //利用 explode 函数分割字符串到数组  代码如下: <?php  $source = "hello1,hello2,hello3,hello4,hello5";//按逗号分离字符串  $hello = explode(',',$source);    for($index=0;$index<count($hello);$index++)  {  echo $