带你重学ES6|Promsie

Promise 让人又恨又爱的存在,恨是因为面试的时候会围绕它出很多题,又绕又头疼,爱是真香,谁都逃不过真香定律。

1、概念

Promise 是异步编程的一个新的解决方案,阮一峰:ECMAScript 6 入门中给出对 promise 的含义是:所谓 Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。

Promise 是一个构造函数,它有两个特点:

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

2、用法

通常我们来说,Promise 主要是解决异步回调地狱。什么是回调地狱?

从网上找了几张图,大家可以感受一下被回调地狱所支配的恐惧:

回调地狱最大的缺点就是代码可读性差,编写费劲。

接下来我们来看一下 Promise 的基本用法:

newPromise((resolve, reject)=>{

...

if(success) {

resolve(value);

}else {

reject(error);

}

})

之前说过,Promise 是一个构造函数,它接收一个函数参数,这个函数中接收两个参数,一个是 resolve 还有一个是 rejected,这两个参数均为函数,并且这两个参数不用自己部署,JS 引擎会自动部署。

resolve 函数的作用是当异步函数成功时,将成功后的值传递出去,同时也是将状态从 pending 变为 resolved,reject 函数的作用是当异步函数失败后,将失败的错误信息传递出去,同时也是将状态从 pending 变为 rejected。

2.1、then()

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

newPromise((resolve, reject) => {

resolve(1);

}).then(

(value) =>console.log(value) // 1

);

newPromise((resolve, reject) => {

reject("出现错误");

}).then(

(error) =>console.log(error) // 出现错误

);

因为 Promise 的对象时立即创建的,所以在 resolve 和 reject 函数之前的操作都会立即执行:

newPromise((resolve, reject) => {

console.log(2);

resolve(1);

}).then(

(value) =>console.log(value) // 2 1

);

Promise 执行 then 方法后会返回回来一个新的 Promise 对象,所以可以采用链式调用。

newPromise((resolve) => {

resolve(1);

})

.then((value) => value + 1)

.then((value) =>console.log(value)); // 2

第一个 then 函数的返回值,可以作为参数传给第二个 then 函数。如果第一个 then 函数返回的依旧是一个 Promise 对象呢?即是一个 Promise 封装的异步操作:

newPromise((resolve) => {

resolve(1);

})

.then(

(value) =>

newPromise((resolve) => {

resolve(3);

})

)

.then((value) =>console.log(value)); // 3

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

2.2、catch()

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

newPromise((resolve, reject)=>{

thrownewError('test')

}).catch(err=>console.log(err)) // Error: test

等同于:

newPromise((resolve, reject)=>{

thrownewError('test')

}).then(null, err=>console.log(err)) // Error: test

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

newPromise((resolve, reject) => {

thrownewError("test");

})

.then(null, (err) => console.log(err)) // Error: test

.catch((err) =>console.log(err));

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

newPromise((resolve, reject) => {

thrownewError("test");

})

.then(null, (err) => {

thrownewError("test1");

})

.catch((err) =>console.log(err)); // Error: test1

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

newPromise((resolve, reject) => {

thrownewError("test");

})

.then()

.catch((err) =>console.log(err)); // Error: test

只要 catch 前任何一个 Promise 报错,那终究会被 catch 截获。

通常情况下,不建议使用 then 函数的第二个参数来进行错误的捕获,如上所说的 catch 写法可以捕获前面 then 方法执行中的错误,也更接近同步的写法(try/catch)。因此,建议总是使用 catch()方法,而不使用 then()方法的第二个参数。

then()返回一个新的 Promise 对象,catch()同样返回一个 Promise 对象,同样可以使用链式调用:

newPromise((resolve, reject) => {

thrownewError("test");

})

.then()

.catch((err) =>console.log(err)) // Error: test

.then(() =>console.log(2)); // 2

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

newPromise((resolve, reject) => {

thrownewError("test");

})

.then()

.catch((err) => {

thrownewError("test1");

}) // Error: test

.then(() =>console.log(2));

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

2.3、finally

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

newPromise((resolve) => {

resolve(1);

})

.then((value) =>console.log(value)) // 1

.finally(() =>console.log(2)); // 2

newPromise(() => {

thrownewError("test");

})

.catch((err) =>console.log(err)) // Error test

.finally(() =>console.log(2)); // 2

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

3、其他 API

除了上述 Promise 原型上的方法外,Promise 还有很多其他的 API。

3.1、 Promise.all

通过字面意思就能看出来,这个方法是‘全部’意思,由此可见可以接受多个 Promise 对象。

该方法接受具有 Iterator 接口并且每个成员都是 Promise 实例的参数,并返回一个新的 Promise 对象。

let p = Promise.all([

newPromise((resolve) => {

resolve(1);

}),

newPromise((resolve) => {

resolve(2);

}),

newPromise((resolve) => {

resolve(3);

}),

]);

并且,只有当接受的参数中所有成员的状态都为 resolved 的时候,p 的状态才会为 resolved,如果有一个成员的状态为 rejected,那 p 的状态就为 rejected。

当所有成员的状态均为 resolved 的时候,会将每个成员 resolved 状态下的值拼成数组传递给 p 的回调函数。

let p = Promise.all([

newPromise((resolve) => {

resolve(1);

}),

newPromise((resolve) => {

resolve(2);

}),

newPromise((resolve) => {

resolve(3);

}),

]);

p.then((result) =>console.log(result)); // [1, 2, 3]

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

let p = Promise.all([

newPromise((resolve) => {

resolve(1);

}),

newPromise((resolve, rejecct) => {

rejecct(2);

}),

newPromise((resolve) => {

resolve(3);

}),

]);

p.catch((err) =>console.log(err));

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

3.2、 Promise.race

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

let p = Promise.race([

newPromise((resolve) => {

setTimeout(() => resolve(1), 100);

}),

newPromise((resolve) => {

setTimeout(() => resolve(2), 50);

}),

newPromise((resolve) => {

setTimeout(() => resolve(3), 200);

}),

]);

p.then((result) =>console.log(result)); // 2

let p = Promise.race([

newPromise((resolve) => {

setTimeout(() => resolve(1), 100);

}),

newPromise((resolve, reject) => {

setTimeout(() => reject(2), 50);

}),

newPromise((resolve) => {

setTimeout(() => resolve(3), 200);

}),

]);

p.catch((err) =>console.log(err)); // 2

3.3、Promise.allSettled

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

let p = Promise.allSettled([

newPromise((resolve) => {

resolve(1);

}),

newPromise((resolve, rejecct) => {

rejecct(2);

}),

newPromise((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.4、Promise.resolve

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

Promise.resolve("foo");

等价于;

newPromise((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.5、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上点个 Star🌟,小伙伴们一定会发现一个问题,我的所有用户名几乎都与番茄有关,因为我真的很喜欢吃番茄 ❤️!!!

想跟车不迷路的小伙还希望可以关注公众号 前端老番茄 或者扫一扫下面的二维码 👇👇👇。

我是一个编程界的小学生,您的鼓励是我不断前进的动力,😄 希望能一起加油前进。

本文使用 mdnice 排版

以上是 带你重学ES6|Promsie 的全部内容, 来源链接: utcz.com/a/32143.html

回到顶部