带你重学ES6 | Promsie

Promise 让人又恨又爱的存在,恨是由于面试的时候会围绕它出不少题,又绕又头疼,爱是真香,谁都逃不过真香定律。前端

一、概念

Promise 是异步编程的一个新的解决方案,阮一峰:ECMAScript 6 入门中给出对 promise 的含义是:所谓 Promise,简单说就是一个容器,里面保存着某个将来才会结束的事件(一般是一个异步操做)的结果。 Promise 是一个构造函数,它有两个特色:git

  1. Promise 有三个状态:pending(进行中)、resolved(已成功)和 rejected(已失败)。而且状态不受外部的影响。
  2. 状态一旦改变就没法修改。只能有两个过程:一个是从 pending 到 resolved 还有一个就是从 pending 到 rejected,不可能从 resolved 到 rejected,一旦成功就不可能再失败了。
  3. Promise 一旦建立就会当即执行,不能中途取消。

二、用法

一般咱们来讲,Promise 主要是解决异步回调地狱。什么是回调地狱? es6

从网上找了几张图,你们能够感觉一下被回调地狱所支配的恐惧: 回调地狱最大的缺点就是代码可读性差,编写费劲。 接下来咱们来看一下 Promise 的基本用法:github

new Promise((resolve, reject)=>{
 ...  if(success) {  resolve(value);  }else {  reject(error);  } }) 复制代码

以前说过,Promise 是一个构造函数,它接收一个函数参数,这个函数中接收两个参数,一个是 resolve 还有一个是 rejected,这两个参数均为函数,而且这两个参数不用本身部署,JS 引擎会自动部署。 resolve 函数的做用是当异步函数成功时,将成功后的值传递出去,同时也是将状态从 pending 变为 resolved,reject 函数的做用是当异步函数失败后,将失败的错误信息传递出去,同时也是将状态从 pending 变为 rejected。面试

2.一、then()

当 Promise 实例建立成功后,能够执行其原型上的 then 方法,then 方法一样接收两个函数参数,第一个是接收的 resolve 回调的结果,第二个是 reject 回调的结果,第二个参数是非必填的。编程

new Promise((resolve, reject) => {
 resolve(1); }).then(  (value) => console.log(value) // 1 ); new Promise((resolve, reject) => {  reject("出现错误"); }).then(  (error) => console.log(error) // 出现错误 ); 复制代码

由于 Promise 的对象时当即建立的,因此在 resolve 和 reject 函数以前的操做都会当即执行:数组

new Promise((resolve, reject) => {
 console.log(2);  resolve(1); }).then(  (value) => console.log(value) // 2 1 ); 复制代码

Promise 执行 then 方法后会返回回来一个新的 Promise 对象,因此能够采用链式调用。promise

new Promise((resolve) => {
 resolve(1); })  .then((value) => value + 1)  .then((value) => console.log(value)); // 2 复制代码

第一个 then 函数的返回值,能够做为参数传给第二个 then 函数。若是第一个 then 函数返回的依旧是一个 Promise 对象呢?便是一个 Promise 封装的异步操做:markdown

new Promise((resolve) => {
 resolve(1); })  .then(  (value) =>  new Promise((resolve) => {  resolve(3);  })  )  .then((value) => console.log(value)); // 3 复制代码

此时第二个 then 函数传入的参数,即为第一个 then 函数返回的 Promise 对象的 resolved 状态下传递的值。也能够说只有第一个 then 返回的 Promise 执行状态成功时,第二个 then 函数才会执行。异步

2.二、catch()

除了 then 函数外,在 Promise 原型上还有一个 catch 函数,此函数时当 Promise 内部异步出现错误的时候即为 rejected 状态时,才执行。

new Promise((resolve, reject)=>{
 throw new Error('test') }).catch(err=>console.log(err)) // Error: test 等同于: new Promise((resolve, reject)=>{  throw new Error('test') }).then(null, err=>console.log(err)) // Error: test 复制代码

当 then 第二个参数和 catch 函数同时存在时,将不会执行 catch 函数:

new Promise((resolve, reject) => {
 throw new Error("test"); })  .then(null, (err) => console.log(err)) // Error: test  .catch((err) => console.log(err)); 复制代码

那此时的 catch 捕获的是哪一个 Promise 的错误呢?捕获的是前一个 Promise 的错误,即 then 函数返回回来的 Promise 错误:

new Promise((resolve, reject) => {
 throw new Error("test"); })  .then(null, (err) => {  throw new Error("test1");  })  .catch((err) => console.log(err)); // Error: test1 复制代码

Promise 的错误有一种相似冒泡机制,当 catch 以前没有没有任何函数截获错误,那终究会被 catch 截获。

new Promise((resolve, reject) => {
 throw new Error("test"); })  .then()  .catch((err) => console.log(err)); // Error: test 复制代码

只要 catch 前任何一个 Promise 报错,那终究会被 catch 截获。 一般状况下,不建议使用 then 函数的第二个参数来进行错误的捕获,如上所说的 catch 写法能够捕获前面 then 方法执行中的错误,也更接近同步的写法(try/catch)。所以,建议老是使用 catch()方法,而不使用 then()方法的第二个参数。 then()返回一个新的 Promise 对象,catch()一样返回一个 Promise 对象,一样可使用链式调用:

new Promise((resolve, reject) => {
 throw new Error("test"); })  .then()  .catch((err) => console.log(err)) // Error: test  .then(() => console.log(2)); // 2 复制代码

当 catch 捕获完错误后,会接着执行下面的 then 方法,当没有错误抛出时,则会跳过 catch,直接执行后面的 then 方法。可是以后的 Promise 出现错误时,以前的 catch 就捕获不到了。

new Promise((resolve, reject) => {
 throw new Error("test"); })  .then()  .catch((err) => {  throw new Error("test1");  }) // Error: test  .then(() => console.log(2)); 复制代码

catch 后面能够链式调用 then 方法,一样也能够调用 catch 方法,后面的 catch 方法是接收前一个 catch 方法所抛出的错误。

2.三、finally

在 ES8 中新加入了一个方法,即 finally,此方法不一样于 then 和 catch,它不跟踪与 Promise 对象状态的改变,即无论 Promise 的最终状态如何,都会执行这个方法,同时 finally 不一样于 then 和 catch 地方就是,它不接受任何参数。

new Promise((resolve) => {
 resolve(1); })  .then((value) => console.log(value)) // 1  .finally(() => console.log(2)); // 2  new Promise(() => {  throw new Error("test"); })  .catch((err) => console.log(err)) // Error test  .finally(() => console.log(2)); // 2 复制代码

finally 一样返回一个新的 Promise 对象,用法和以前的 then 和 catch 同样,这块就不作过多的讲解了。

三、其余 API

除了上述 Promise 原型上的方法外,Promise 还有不少其余的 API。

3.一、 Promise.all

经过字面意思就能看出来,这个方法是‘所有’意思,因而可知能够接受多个 Promise 对象。 该方法接受具备 Iterator 接口而且每一个成员都是 Promise 实例的参数,并返回一个新的 Promise 对象。

let p = Promise.all([
 new Promise((resolve) => {  resolve(1);  }),  new Promise((resolve) => {  resolve(2);  }),  new Promise((resolve) => {  resolve(3);  }), ]); 复制代码

而且,只有当接受的参数中全部成员的状态都为 resolved 的时候,p 的状态才会为 resolved,若是有一个成员的状态为 rejected,那 p 的状态就为 rejected。 当全部成员的状态均为 resolved 的时候,会将每一个成员 resolved 状态下的值拼成数组传递给 p 的回调函数。

let p = Promise.all([
 new Promise((resolve) => {  resolve(1);  }),  new Promise((resolve) => {  resolve(2);  }),  new Promise((resolve) => {  resolve(3);  }), ]); p.then((result) => console.log(result)); // [1, 2, 3] 复制代码

当有一个成员的状态为 rejected 的时候,则会将第一个 rejected 状态的值返给 p 的 catch 方法。

let p = Promise.all([
 new Promise((resolve) => {  resolve(1);  }),  new Promise((resolve, rejecct) => {  rejecct(2);  }),  new Promise((resolve) => {  resolve(3);  }), ]); p.catch((err) => console.log(err)); 复制代码

若是有一个成员为 rejected 状态,而且自身调用了 catch 方法,那将不会走 p 对象的 catch 方法,这一点要注意。

3.二、 Promise.race

race 翻译成中文是竞赛的意思,他表示多个 Promise 对象,哪一个成员率先改变状态,那 Promise.race 返回的 Promise 对象的状态变为何,并将值转递给 p 的回调函数,它和 Promise.all 接收的参数同样。

let p = Promise.race([
 new Promise((resolve) => {  setTimeout(() => resolve(1), 100);  }),  new Promise((resolve) => {  setTimeout(() => resolve(2), 50);  }),  new Promise((resolve) => {  setTimeout(() => resolve(3), 200);  }), ]); p.then((result) => console.log(result)); // 2  let p = Promise.race([  new Promise((resolve) => {  setTimeout(() => resolve(1), 100);  }),  new Promise((resolve, reject) => {  setTimeout(() => reject(2), 50);  }),  new Promise((resolve) => {  setTimeout(() => resolve(3), 200);  }), ]); p.catch((err) => console.log(err)); // 2 复制代码

3.三、Promise.allSettled

该方法是 ES2020 新加入的,和 all 同样,返回一个新的 Promise 对象,接收一组 Promise 对象,可是和 all 区别的是,当无论每一个成员的 Promise 是什么状态,只要执行结束,则返回的 Promise 对象就会执行结束。

let p = Promise.allSettled([
 new Promise((resolve) => {  resolve(1);  }),  new Promise((resolve, rejecct) => {  rejecct(2);  }),  new Promise((resolve) => {  resolve(3);  }), ]); p.then((value) => console.log(JSON.stringify(value))); // [{status:"fulfilled",value:1},{status:"rejected",reason:2},{status:"fulfilled",value:3}] 复制代码

有时候异步请求并不在乎是否可以成功,这个时候这个方法就很符合场景了,而且返回一个数组,数组中每一个对象有两个状态,一个是 fulfilled,另外一个是 rejected,返回以后能够进行筛选,查看错误信息。

3.四、Promise.resolve

将一个对象转化为一个 Promise 对象。

Promise.resolve("foo");
等价于; new Promise((resolve) => resolve("foo")); 复制代码

当 Promise.resolve 的参数是一个 Promise 实例时,原封不动的返回这个实例:

let p = new Promsie((resolve) => resolve(1));
let p1 = Promise.resolve(p); p === p1; // true 复制代码

当参数是一个 thenable 对象时,即含有 then 方法的对象时,会返回一个 Promise 对象,并当即执行 then 方法。

let thenable = {
 then: function (resolve, reject) {  resolve(1);  }, }; let p = Promise.resolve(thenable); p.then((value) => console.log(value)); // 1 复制代码

当参数是否是一个 thenable 对象时,因为参数不是一个异步的,因此当 Promise.resolve 后,直接的状态就是 resolved 的状态,因此 then 后就会输出原值。

1. 参数是个普通对象
let obj = {  name: '1' } let p = Promise.resolve(obj); p.then(value=>console.log(value)) // {name: '1'} 2. 参数是个基本数据类型 let num = '1'; let p = Promise.resolve(num); p.then(value=>console.log(value)) // 1 复制代码

当不传参数的时候,返回的就是一个带有 resolved 状态的 Promise 对象。

3.五、Promise.reject

返回一个状态为 rejected 的 Promise 对象,传入的参数做为错误信息做为后续方法传递的参数。

let num = "1";
let p = Promise.reject(num); p.then(null, (err) => console.log(err)); // 1 复制代码

当参数是 thenable 对象时,返回的不是 error 信息而是 thenable 对象。

let thenable = {
 then: function (resolve, reject) {  reject(1);  }, }; let p = Promise.reject(thenable); p.then(null, (err) => console.log(err === thenable)); // true 复制代码

后语

相关文章:

以为还能够的,麻烦走的时候能给点个赞,你们一块儿学习和探讨!

还能够关注个人博客但愿能给个人github上点个Start,小伙伴们必定会发现一个问题,个人全部用户名几乎都与番茄有关,由于我真的很喜欢吃番茄❤️!!!

想跟车不迷路的小伙还但愿能够关注公众号 前端老番茄 或者扫一扫下面的二维码👇👇👇。

我是一个编程界的小学生,您的鼓励是我不断前进的动力,😄但愿能一块儿加油前进。

本文使用 mdnice 排版

相关文章
相关标签/搜索