美文网首页代码世界
node.js 如何使用Promise

node.js 如何使用Promise

作者: CodingCode | 来源:发表于2017-11-21 16:34 被阅读32次

Promise提供了一种异步执行模式。
注意一点Promise的执行仍然是异步方式的,并没有改变成同步执行模式,只不过让代码写起来读起来像是同步执行一样。

Promise是使用主要分两步:

第一步,定义Promise

var p = new Promise((resolve, reject) => {
    //
    // promise body
    // if (...) {
    //  resolve(value)
    // } else {
    //  reject(reason)
    // }
    //
});

注意3点:

  1. 定义Promise时需要一个参数,这个参数是一个函数,我把它叫做Promise函数体
function (resolve, reject) { ... }
  1. Promise函数体接受两个参数
// 参数resolve和reject也是一个函数,原型定义如下:
// function resolve(value) {...}
// function reject(reason) {...}
  1. Promise函数体不需要返回值。
    Promise函数体的返回是通过回调参数函数resolve和reject来标记函数成功失败;如果成功则调用resolve()函数,如果失败则调用reject()函数。

请比较Promise函数体原型,和Promise.then函数体原型,他们是一样的,他们应该就是一样的。

第二步,定义resolve和reject参数函数

前面Promise函数体里面用到了resolve和reject函数,但彼时只是形参而已,这两个函数并没有被定义出来;那么他们在哪里定义的呢,Promise的then和catch函数就是真正用来定义resolve和reject函数体的。
函数then和catch本身是Promise的一个内置函数(注意区分两个概念:then函数,和then函数的参数函数):

promise.then(function(value) {
    // body
}).catch(function(reason) {
    // body
})

then和catch函数都接收一个函数作为参数,这个参数函数原型定义

function (value) { ... }

我们可以看到这个函数原型定义和Promise定义时的参数resolve和reject的原型是一样的,这就明白了吧。

  1. promise.then()函数的返回值是一个Promise对象,这是为了构造then()函数链。
    promise.catch()函数的返回值也是一个Promise对象(尽管我们通常不会使用这个对象)。
  2. then()函数参数函数的返回值,是作为下一个then函数链的输入参数。
    如果返回值是一个Promise对象除外,后面有分析。
  3. 这里要弄清楚then()函数的返回值,和then函数参数函数的返回值。
    3.1 then函数的定义是Promise内置定义的,它的代码,输入输出都由Promise实现,不是用户设置的;也就是用户根本看不见then函数的返回语句。
    3.2 then函数参数函数是传递给then()的那个用户定义函数,这个函数体是由用户定义的,所以其返回值都是由用户提供的,如下代码可以返回一个数字,字符串,或者对象都可以。
     1  function foo(param) {
     2      var p = new Promise((resolve, reject) => {
     3          if (param == 1) { 
     4              resolve(param)
     5          }
     6          else {
     7              reject("invalid value " + param)
     8          }
     9      });
    10      return p
    11  }
    12
    13  foo(1)
    14  .then((value) => {
    15      console.log("then-1: " + value);
    16      return "2"
    17  })
    18  .then((value) => {
    19      console.log("then-2: " + value);
    20  })
    21  .catch((reason) => {
    22      console.log("catch: " + reason);
    23  })

运行结果:

$ node p.js
then-1: 1
then-2: 2
  • 第15行then-1的输出 value是第4行resolve函数的参数值。
  • 第19行then-2的输出 value是第16行return的值,即前一个then函数体的返回值作为下一个then函数体的参数。

我们看到在then()链中,一个then函数体里的返回值是作为下一个then函数体的参数使用的,那么如何在一个then函数体里面标记一个reject状态呢,因此此时也不是一个Promise定义函数,也就没有reject函数形参可用。

  1. 办法1: 也是最直白的办法是抛出异常,还是以上面代码为例:
    ...
    13  foo(1)
    14  .then((value) => {
    15      console.log("then-1: " + value);
    16      throw "2"
    17  })
    18  .then((value) => {
    19      console.log("then-2: " + value);
    20  })
    21  .catch((reason) => {
    22      console.log("catch: " + reason);
    23  })

我们把第16行的return语句改成了throw语句,运行结果:

$ node p.js  
then-1: 1
catch: 2
  1. 办法2:再定义一个Promise并返回
    13  foo(1)
    14  .then((value) => {
    15      console.log("then-1: " + value);
    16      return new Promise((resolve, reject) => {
    17          if (value == 1) { 
    18              console.log("then-1-1: " + value);
    19              resolve(100)
    20          }
    21          else {
    22              reject("invalid value " + value)
    23          }
    24      });
    25  })
    26  .then((value) => {
    27      console.log("then-2: " + value);
    28  })
    29  .catch((reason) => {
    30      console.log("catch: " + reason);
    31  })

在then-1里面可以返回一个值给then-2使用,或者抛出一个异常直接到catch分支,也可以定义一个Promise对象,在新定义的Promise函数体里面使用resolve或者reject以决定走then-2还是catch异常分支。

$ node p.js  
then-1: 1
then-1-1: 1
then-2: 100
  • 第19行resolve的结果是吧值100传给then-2作为参数。
  • 注意这里then函数参数函数里面返回一个具体值和返回一个Promise对象的差异
    • 如果返回一个普通值,则把值直接传给后面的一个then作为参数。
    • 如果返回一个Promise对象,则根据promise的resolve和reject决定后面的then参数值。

补充一点,其实then()函数的原型有两个参数

po.then(function(value) {  // success },
        function(value) {  // failure }
       );

分别对应Promise最终状态是resolve和reject,而通常为了构造Promise链才只提供一个参数函数,即resolve参数,而把所有的reject函数参数统一到catch函数里面。

Promise相关代码的执行顺序

看一个例子来说明问题,

console.log("main-1");
function foo(param) {
    var p = new Promise((resolve, reject) => {
        console.log("promise-1");
        if (param == 1) { 
            resolve("1")
        } else {
            reject("2")
        }
        console.log("promise-2");
    });
    return p
}

console.log("main-2");

foo(1).then((value) => {
    console.log("then-1");
}).catch((reason) => {
    console.log("catch-1");
})

console.log("main-3");

执行结果如下:

$ node promise.js
main-1
main-2
promise-1
promise-2
main-3
then-1

所以

  1. Promise函数体是在Promise对象创建的时候就被执行了,可以理解为Promise函数是同步执行的。
    由此我们知道resolve或者reject函数已经在Promise函数体内被调用了,而此时resolve和reject的值并没有被定义了,怎么办?其实这就是Promise机制实现的功能,可是先调用一个未定义的函数,等将来函数被定义的时候(then())在真正执行函数体。
  2. then/catch函数体并不是在then/catch被调用的时候执行的,而是在后面的某一个异步时间点被执行,这也是Promise机制实现的功能。
    因此定义Promise时指定的函数体是在当场就执行的,而定义then()时指定的函数体不是当场执行,而是在之后以异步的方式执行的。

简单的说

  1. Promise对象创建的时候,立刻执行Promise函数体,同时会标记将来是执行resolve还是reject
    多说一句Promise对象的最终状态只有两个要么是resolved,要么是rejected,所以如果在Promise函数体里面同时调用了resolve和reject(注意Promise函数体里面不管是调用了resolve还是reject,都不结束函数,而会继续执行后面的代码),谁先调用谁有效,后面调用的无效,因为第一个调用就已经改变了Promise的最终状态。举个例子:
     1  function foo(param) {
     2      var p = new Promise((resolve, reject) => {
     3          resolve("succ");
     4          reject("fail");
     5          console.log("hello");
     6          return "any";
     7          console.log("world");
     8      });
     9      return p
    10  }
    11
    12  foo(1)
    13  .then((value) => {
    14      console.log("then-1: " + value);
    15  })
    16  .catch((reason) => {
    17      console.log("catch: " + reason);
    18  })

运行结果为:

$ node p.js
hello
then-1: succ
  • 第3行resolve把promise最终状态职位resolved.
  • 第4行reject代码无效,因为Promise的最终状态已经被置为resolved了,此时再置成rejected自然无效。
  • 第5行log代码正常执行
  • 第6行return返回,这个值不知道返回到哪里去了。
  • 第7行没有执行,因为第6行已经返回了。
  1. then/catch负责注册这些函数体到对应的resolve/reject函数链上,而不会马上就执行他们,只是注册。
  2. 对他们的执行是在稍后以异步事件的方式回调的;具体的回调时间是不确定的。

后面还有Promise.all()等没有研究,主要是暂时没有用到;以后有机会再整理。

相关文章

  • node.js 如何使用Promise

    Promise提供了一种异步执行模式。注意一点Promise的执行仍然是异步方式的,并没有改变成同步执行模式,只不...

  • Node.js 中 TimeoutPromise 封装

    NodejsLearning 使用 Node.js 的同学, 一定免不了使用 Promise, 说到 Promis...

  • node.js Promise链的参数传递

    node.js Promise链的参数传递 介绍Promise链如何把前一个Promise的返回结果传给后一个Pr...

  • vue-Axios

    基于Promise 用于浏览器和 node.js的与服务器通讯的库支持Promise API安装 使用 npm: ...

  • JS基础:异步编程之Promise、async-await

    目录一. Promise 1. 为什么要使用Promise 2. Promise是什么 3. 如何使用Promis...

  • Promise原理解析

    Promise原理解析 标签(空格分隔): Node.js Promise原理解析 简介 Promise 对象用于...

  • Promise 源代码解析

    本文从 Promise 的源代码入手,分析Promise是如何实现的。并不涉及Promise如何使用.适用于已经熟...

  • axios

    axios简介 axios基于 Promise 的 HTTP 请求客户端,可同时在浏览器和 node.js 中使用...

  • 学习Axios

    基于 promise 的 HTTP 库,可以用在浏览器和 node.js 安装 使用 npm: 实例 1. 发送一...

  • Promise,async/await,yield/genera

    Promise是什么?如何使用? 我们起点低一点,先从Promise说起吧。。我们先看Promise的调用eg1:...

网友评论

    本文标题:node.js 如何使用Promise

    本文链接:https://www.haomeiwen.com/subject/ktkwvxtx.html