ES6读书简记·Promise

CY 2019年01月06日 679次浏览

Promise解决了回调地狱的问题

设计到一个概念"事件循环",可以使用动画来了解

基本用法

先声明一个Promise

// 需要传递一个函数,函数里面会接收到两个参数,这两个参数是两个函数
const promise = new Promise(function(resolve, reject) {
  // ... some code
    
  if (/* 异步操作成功 */){
    resolve(value); // 从“未完成”变为“成功”
  } else {
    reject(error); // 从“未完成”变为“失败”
  }
});

然后使用then方法

promise.then(function(value) { // 对应 resolve(value)
  // success
}, function(error) { // 对应 reject(error),这个参数可以没有
  // failure
});

Promise新建后会立即执行,then里面的回调将在本轮“事件循环”结尾执行

如果resolve或者reject函数中有参数,这个参数将会被传递给回调函数。

如果参数为另一个Promise,那么当前Promise的状态将由另一个Promise决定

调用resolvereject方法的时候,并不会终结Promise参数中的函数的执行。

Promise.prototype.then()

then方法为Promise实例添加状态改变时的回调函数。

then方法返回一个新的Promise实例,注意是新的,所以可以链式调用。

如果前一个then回调的返回值返回的也是一个Promise,那么下一个then会等待这个新的Promise对象的状态发生变化。

Promise.prototype.catch()

Promise.prototype.catch方法是.then(null, rejection).then(undefined, rejection)的别名,用于指定发生错误时的回调函数。

catch可以捕获前面执行的错误,不管是有多少个then,只要前面没有处理错误,都会被后面的catch处理,所以说then就不需要第二个参数了,所有的异常全部交给catch来处理。

Promise内部出现了错误,并不会影响Promise外面代码的执行,Promise会吃掉错误。

由于catch依然返回一个Promise,所以catch方法后面还可以使用then,但是如果后面的then抛出的错误,就和前面的catch没有关系了。

Promise.prototype.finally()

finally方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。该方法是 ES2018 引入标准的。

finally的回调函数不接受任何参数

Promise.all()

可以理解为这个方法是&&运算符

Promise.all()方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。

传入一个数组或者Iterator作为参数,数组中应该全部都是Promise实例,如果不是Promise实例,就会自动转换成Promise实例。

当数组中的Promise状态全部变成fulfilled状态的时候,Promise.all()才会变成fulfilledthen的回调接收到一个数组,数组里面是这个Promise数组的返回值。

当数组中有一个rejectedPromise.all()就变成了rejected,第一个reject的返回值,会传递给回调函数(catch或者then的第二个参数)。

如果说,数组里面有一个Promise已经处理了catch,那么这个Promise出现错误的时候就不会交给Promise.all()来处理了,因为处理完catchPromise已经变成了fulfilled状态。

Promise.race()

Promise.race()方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。

Promise.race()这个方法的回调中只能接收到率先改变的哪个Promise,不管变成什么状态,只要它率先改变了状态,就会调用回调。

利用这个特性可以做操作超时:

Promise.race([
    new Promise((resolve, reject) => void setTimeout(x => resolve("访问网络资源"), 6000)),
    new Promise((resolve, reject) => void setTimeout(x => reject("访问网络资源超时..."), 1000)) 
])
    .then(x => void console.log("访问成功", x))
    .catch(x => void console.log("访问失败", x));
// 访问失败 访问网络资源超时...

Promise.allSettled()

不关心异步操作的结果,只关心这些操作有没有结束。这时,Promise.allSettled()方法就很有用

Promise.allSettled()方法接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例。只有等到所有这些参数实例都返回结果,不管是fulfilled还是rejected,包装实例才会结束。该方法由 ES2020 引入。

这个方法只会调用resolve回调,不会调用reject

回调函数中接受到的参数是一个对象数组,对象的属性如下:

status:属性的值只可能是字符串fulfilled或字符串rejected

valuefulfilled时,对象有value属性

reasonrejected时有reason属性

Promise.any()

这个可以理解为是||运算符

该提案已经进入第三阶段

参数也是一个数组,只要参数实例有一个变成fulfilled状态,包装实例就会变成fulfilled状态;如果所有参数实例都变成rejected状态,包装实例就会变成rejected状态

这个方法抛出的错误,不是一个一般的错误,是AggregateError实例,相当于一个数组,存储了每一个Promiserejected后抛出的错误。

Promise.resolve()

有时需要将现有对象转为 Promise 对象,Promise.resolve()方法就起到这个作用

转换成Promise对象有以下几种参数:

参数本身就是一个Promise实例:不变化

参数是一个thenable对象:也就是说这个对象里面有then方法,会先将这个对象转换成Promise对象,然后执行对象的then方法。

参数是一个普通的原始值:会直接将原始值传递给then的回调。

不带参数:直接返回一个resolved状态的Promise对象。

then回调中的语句是在本轮事件循环结束的时候执行,不会到下一轮事件循环中执行。

Promise.reject()

Promise.reject(reason)方法也会返回一个新的 Promise 实例,该实例的状态为rejected

Promise.reject()方法的参数,会原封不动地作为reject的理由,变成后续方法的参数,而不会转换成Promise对象

Promise.try()

这也是一个新的提案

目的是让同步的方法也可以轻松的使用Promise来包装,并且不会变成异步执行(本轮事件循环末尾执行),使用try后,catch方法不仅可以捕捉到异步的异常,也可以捕获同步的异常

微任务

使用setTimeout这样的方法创建的任务会到下一轮事件循环中执行,但是then方法中的回调属于微任务,他会到本轮事件循环末尾执行,也就是会直接追加到本轮事件循环,而不会到下一轮事件循环中执行。所以,then的回调会永远早于setTimeout执行。