理解ES6中的Promise
in Note with 8 comments
理解ES6中的Promise
in Note with 8 comments

这里不说Promise是什么,网上有各种各样的版本,所以在这里,我只想记录一下 Promise 的执行过程和记录下来使用注意事项的东西,再添加了点个人理解而已

对了,我还写了一篇《理解ES6中的Generator

工作流

先上代码

function asyncFunction(){
  return new Promise(function(resolve,reject){
    setTimeout(function(){
      resolve('Async Hello World');
    },16);
  });
}

asyncFunction().then(function(value){
  console.log(value);
}).catch(function(error){
  console.log(error);
});

在代码中,我们用到了构造函数 Promise 来创建一个新建新promise对象;然后,执行promise对象时,执行用.then 调用返回值时的回调函数

asyncFunction 这个函数会返回promise对象, 对于这个promise对象,我们调用它的then方法来设置resolve后的回调函数, catch 方法来设置发生错误时的回调函数

该promise对象会在setTimeout之后的16ms时被resolve, 这时 then 的回调函数会被调用,并输出Async Hello world

状态

new Promise实例化的promise对象有以下三个状态。左侧为在 ES6 Promises 规范中定义的术语, 而右侧则是在 Promises/A+ 中描述状态的术语

1、 "has-resolution" - Fulfilled
resolve(成功)时。此时会调用 onFulfilled

2、 "has-rejection" - Rejected
reject(失败)时。此时会调用 onRejected

3.、"unresolved" - Pending
既不是resolve也不是reject的状态。也就是promise对象刚被创建后的初始化状态等

另外,Fulfilled和Rejected这两个中的任一状态都可以表示为Settled(不变的)

4、Settled
resolve(成功) 或 reject(失败)。

实战

语法糖

Promise.resolve

Promise.resolve(42); 可以认为是以下代码的语法糖

new Promise(function(resolve){
  resolve(42);
});

在这段代码中的 resolve(42); 会让这个promise对象立即进入确定(即resolved)状态,并将 42 传递给后面then里所指定的 onFulfilled 函数

方法 Promise.resolve(value); 的返回值也是一个promise对象

Promise.reject

Promise.reject(new Error("出错了")) 就是下面代码的语法糖

new Promise(function(resolve,reject){
  reject(new Error("出错了"));
});

Thenable

ES6 Promises里提到了Thenable这个概念,简单来说它就是一个非常类似promise的东西

就像我们有时称具有 .length方法的非数组对象为Array like一样,thenable指的是一个具有 .then 方法的对象

这种将thenable对象转换为promise对象的机制要求thenable对象所拥有的 then 方法应该和Promise所拥有的 then方法具有同样的功能和处理过程,在将thenable对象转换为promise对象的时候,还会巧妙的利用thenable对象原来具有的 then 方法

then是异步的

.then 中指定的方法调用是异步进行的,如下

var promise = new Promise(function (resolve){
  console.log("inner promise");
  resolve(42);
});

promise.then(function(value){
  console.log(value);
});

console.log("outer promise");

执行后是

inner promise
outer promise
42

为了避免同时使用同步、异步调用可能引起的混乱问题,Promise在规范上规定Promise只能使用异步调用方式,所以Promise保证了每次调用都是以异步方式进行的

链式写法

promise可以写成方法链的形式,如下

aPromise.then(function taskA(value){
// task A
}).then(function taskB(vaue){
// task B
}).catch(function onRejected(error){
  console.log(error);
});

从代码上乍一看, aPromise.then(...).catch(...) 像是针对最初的 aPromise 对象进行了一连串的方法链调用

然而实际上不管是 then 还是 catch 方法调用,都返回了一个新的promise对象

我们判断一下两个方法返回的到底是不是新的promise对象,如下

var aPromise = new Promise(function (resolve) {
  resolve(100);
});
var thenPromise = aPromise.then(function (value) {
  console.log(value);
});
var catchPromise = thenPromise.catch(function (error) {
  console.error(error);
});

console.log(aPromise === thenPromise);
console.log(thenPromise === catchPromise);

执行返回

false
false
100

=== 是严格相等比较运算符,我们可以看出这三个对象都是互不相同的,这也就证明了 thencatch 都返回了和调用者不同的promise对象

Promise.all

Promise.all 接收一个 promise对象的数组作为参数,当这个数组里的所有promise对象全部变为resolve或reject状态的时候,它才会去调用 .then 方法

function timerPromisefy(delay) {
  return new Promise(function(resolve) {
    setTimeout(function() {
      resolve(delay);
    }, delay);
  });
}
var startDate = Date.now();

Promise.all([
  timerPromisefy(1),
  timerPromisefy(32),
  timerPromisefy(64),
  timerPromisefy(128)
]).then(function(values) {
  console.log(Date.now() - startDate + 'ms');
  console.log(values);
});

执行返回

129ms
[ 1, 32, 64, 128 ]

这个promise对象数组中所有promise都变为resolve状态的话,至少需要128ms

从上述结果可以看出,传递给 Promise.all 的promise并不是一个个的顺序执行的,而是同时开始、并行执行的

Promise.race

Promise.race的使用方法和Promise.all一样,接收一个promise对象数组为参数,当其中一个promise对象进入 FulFilled 或者 Rejected 状态的话,就会去调用 .then 方法

function timerPromisefy(delay) {
  return new Promise(function(resolve) {
    setTimeout(function() {
      resolve(delay);
    }, delay);
  });
}

Promise.race([
  timerPromisefy(1),
  timerPromisefy(32),
  timerPromisefy(64),
  timerPromisefy(128)
]).then(function(value) {
  console.log(value);
});

执行返回

1

上面的代码创建了4个promise对象,这些promise对象会分别在1ms,32ms,64ms和128ms后变为确定状态,即FulFilled,并且在第一个变为确定状态的1ms后, .then 注册的回调函数就会被调用,这时候确定状态的promise对象会调用 resolve(1) 因此传递给 value 的值也是1,控制台上会打印出1

需要注意的是

Promise.race 在第一个promise对象变为Fulfilled之后,并不会取消其他promise对象的执行

补充

在 onFulfilled 中发生异常的话,在 onRejected 中是捕获不到这个异常的

then 中产生的异常能在 .catch 中捕获

需要分场合使用

总结

之前看《你不知道的JavaScript(中卷)》的时候已经涉及到,看得不太懂,所以这次再看一次迷你书,然后又翻了一下中卷,全懂了,大概就这样,感觉没啥好总结的,写个demo或许会更好

内容比较多,至此~

参考

基本上全书已看,也把书中的一些参考也看了点

Responses
  1. COOL! 谢谢分享,学到了 then 和 catch 会返回一个新的 Promise 对象。

    另外 Promise.all 虽然是同时开始并发执行,但是返回的数组顺序跟输入保持一致,在需要保证返回顺序的情况下很好用。

    Reply
  2. 坨坨肉

    Promise 配合 async await 更好用

    Reply
    1. @坨坨肉

      一个是链式写法,一个是同步写法,没有什么配合什么好的~

      Reply
      1. @Chakhsu

        并不是 async await 才是异步最好的方式 你的promise 无非返回的是一个异步函数,在真正的产出环境(其前后端分离)中,promise就会出现回调的厄运,所以建议还是全面的使用es7的提案

        Reply
        1. @devmsg

          大佬~你说得对!

          Reply
  3. 咦,这个主题真心感觉也很不错呀!

    Reply
    1. @明月登楼

      谢谢支持~😁

      Reply
      1. Avan
        @Chakhsu

        怎么把html页面单独拿出来哦 不会php

        Reply