Promise原理及实现
Posted 橘猫吃不胖~
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Promise原理及实现相关的知识,希望对你有一定的参考价值。
Promise原理及实现
- 1 Promise核心逻辑实现
- 2 加入异步逻辑
- 3 then方法添加多次调用逻辑
- 4 链式调用then方法
- 5 Promise错误捕获
- 6 then方法参数设置为可选
- 7 实现Promise.all
- 8 实现Promise.resolve
- 9 实现Promise.race
- 10 实现finally方法
- 11 实现catch方法
- 12 全部代码展示
1 Promise核心逻辑实现
Promise对象是一个原生的javascript对象,是一种异步编程的解决方案,它通过一个回调,可以避免更多的回调,接下来说明其原理及实现。
下面一段代码是Promise的基本使用:
new Promise((resolve, reject) =>
resolve("成功");
reject("失败");
)
Promise.then(value => , reason => )
从上面的代码中,我们可以分析出一些关键点:
- Promise创建时需要使用
new
关键字,那么我们可以知道Promise就是一个类; - 在执行这个类的时候,需要传递一个执行器进去,这个执行器会立即执行;
- 在执行器中有两个参数,
resolve
和reject
,它们都是函数,用来改变Promise中的状态; - Promise中有三种状态,分别是:成功
fulfilled
、失败rejected
和等待pending
,状态只能从pending
—>fulfilled
,或者pending
—>rejected
,状态一旦确定就不可以更改; resolve
和reject
函数是用来更改状态的,其中,resolve
将状态更改为fulfilled
,reject
将状态更改为rejected
;then
方法接收两个函数作为参数,它需要判断状态,如果成功调用第一个回调函数,如果失败调用第二个回调函数,并且then
方法是被定义在原型对象中的,第一个回调函数的参数为成功之后的值,第二个回调函数的参数为失败之后的原因;
接下来我们根据上面分析出的内容,一步一步实现我们自己的Promise。
首先创建一个类,为constructor
构造函数传入一个执行器,因为执行器需要立即执行,因此在构造函数中调用该执行器。
class MyPromise
constructor(executor) // 接收一个执行器
executor(); // 执行器会立即执行
在执行器中有两个参数resolve
和reject
,它们都是函数,因此在类中创建两个箭头函数resolve
和reject
,在执行器executor
中使用this
来调用它们。
为什么使用箭头函数:
注意,我们在Promise中调用resolve
和reject
是直接调用的,如果将它们写成普通函数,那么会将this
指向window
或者undefined
,如果我们写成箭头函数,那么它们的this
就会指向类的实例对象。
class MyPromise
constructor(executor) // 接收一个执行器
executor(this.resolve, this.reject); // 执行器会立即执行
resolve = () =>
reject = () =>
resolve
和reject
这两个函数是用来改变状态的,因此我们将状态定义在类的外面,因为它们会被频繁使用到。在类中我们默认定义状态status
是等待pending
,当调用resolve
时,状态改为成功,当调用reject
时,状态改为失败。并且状态一旦确定不可更改,因此我们要在两个函数中判断当前状态是否为等待,不是则返回。
const PENDING = "pending"; // 等待
const FULFILLED = "fulfilled"; // 成功
const REJECTED = "rejected"; // 失败
class MyPromise
constructor(executor) // 接收一个执行器
executor(this.resolve, this.reject); // 执行器会立即执行
status = PENDING; // 状态默认为pending等待
resolve = () =>
if (this.status !== PENDING) return; // 当状态不是等待,直接返回
this.status = FULFILLED; // 将状态改为成功
reject = () =>
if (this.status !== PENDING) return; // 当状态不是等待,直接返回
this.status = REJECTED; // 将状态改为失败
Promise中的then
方法有两个参数,当状态成功调用第一个,状态失败调用第二个,因此内部需要使用if
来判断状态。调用成功或者失败函数时,需要为其传入参数,那么我们知道成功的值是由resolve
传递来的,失败的原因是由reject
传递来的,因此我们在Promise中声明两个属性存放两个值。
const PENDING = "pending"; // 等待
const FULFILLED = "fulfilled"; // 成功
const REJECTED = "rejected"; // 失败
class MyPromise
constructor(executor) // 接收一个执行器
executor(this.resolve, this.reject); // 执行器会立即执行
status = PENDING; // 状态默认为pending等待
value = undefined; // 成功之后的值
reason = undefined; // 失败之后的原因
resolve = value => // value是成功之后的值
if (this.status !== PENDING) return; // 当状态不是等待,直接返回
this.status = FULFILLED; // 将状态改为成功
this.value = value; // 将成功的值传递
reject = reason => // reason是失败之后的原因
if (this.status !== PENDING) return; // 当状态不是等待,直接返回
this.status = REJECTED; // 将状态改为失败
this.reason = reason; // 将失败的值传递
then(successCallback, failCallback) // then方法有两个参数
if (this.status === FULFILLED) // 成功调用第一个回调函数
successCallback(this.value);
else if (this.status === REJECTED) // 失败调用第二个回调函数
failCallback(this.reason);
到这里我们就实现了一个最简单的Promise了。
2 加入异步逻辑
上面我们实现的Promise,实际上并没有考虑异步情况,比如说下面的代码中,2秒后调用成功的回调,如果这时调用then
方法,那么当前的状态是等待pending
,但是我们并没有判断状态是pending
时的情况。
new Promise((resolve, reject) =>
setTimeout(() =>
resolve("成功");
, 2000);
)
Promise.then(value => , reason => )
因此在then
方法中,我们应该判断当状态是等待的情况。当状态是等待时,我们没有办法调用成功或者失败的回调,这时我们需要将成功回调和失败回调储存起来。
const PENDING = "pending"; // 等待
const FULFILLED = "fulfilled"; // 成功
const REJECTED = "rejected"; // 失败
class MyPromise
constructor(executor) // 接收一个执行器
executor(this.resolve, this.reject); // 执行器会立即执行
status = PENDING; // 状态默认为pending等待
value = undefined; // 成功之后的值
reason = undefined; // 失败之后的原因
successCallback = undefined; // 成功的回调函数
failCallback = undefined; // 失败的回调函数
resolve = value => // value是成功之后的值
if (this.status !== PENDING) return; // 当状态不是等待,直接返回
this.status = FULFILLED; // 将状态改为成功
this.value = value; // 将成功的值传递
reject = reason => // reason是失败之后的原因
if (this.status !== PENDING) return; // 当状态不是等待,直接返回
this.status = REJECTED; // 将状态改为失败
this.reason = reason; // 将失败的值传递
then(successCallback, failCallback) // then方法有两个参数
if (this.status === FULFILLED) // 成功调用第一个回调函数
successCallback(this.value);
else if (this.status === REJECTED) // 失败调用第二个回调函数
failCallback(this.reason);
else // 当状态为等待时,将成功回调和失败回调存储起来
this.successCallback = successCallback;
this.failCallback = failCallback;
将成功回调和失败回调存储起来之后,我们则要在resolve
和reject
方法中判断是否存在成功或者失败的回调,如果存在,则将其调用。
const PENDING = "pending"; // 等待
const FULFILLED = "fulfilled"; // 成功
const REJECTED = "rejected"; // 失败
class MyPromise
constructor(executor) // 接收一个执行器
executor(this.resolve, this.reject); // 执行器会立即执行
status = PENDING; // 状态默认为pending等待
value = undefined; // 成功之后的值
reason = undefined; // 失败之后的原因
successCallback = undefined; // 成功的回调函数
failCallback = undefined; // 失败的回调函数
resolve = value => // value是成功之后的值
if (this.status !== PENDING) return; // 当状态不是等待,直接返回
this.status = FULFILLED; // 将状态改为成功
this.value = value; // 将成功的值传递
this.successCallback && this.successCallback(this.value); // 如果成功回调存在,则调用
reject = reason => // reason是失败之后的原因
if (this.status !== PENDING) return; // 当状态不是等待,直接返回
this.status = REJECTED; // 将状态改为失败
this.reason = reason; // 将失败的值传递
this.failCallback && this.failCallback(this.reason); // 如果失败回调存在,则调用
then(successCallback, failCallback) // then方法有两个参数
if (this.status === FULFILLED) // 成功调用第一个回调函数
successCallback(this.value);
else if (this.status === REJECTED) // 失败调用第二个回调函数
failCallback(this.reason);
else // 当状态为等待时,将成功回调和失败回调存储起来
this.successCallback = successCallback;
this.failCallback = failCallback;
这是我们就处理了异步的情况了。
3 then方法添加多次调用逻辑
Promise的then
方法可以调用多次,我们接着处理这部分。
let promise = new Promise((resolve, reject) => )
promise.then(value => )
promise.then(value => )
promise.then(value => )
如果多次调用了then
方法,就需要考虑两种情况:同步情况和异步情况。如果是同步情况,那么直接就可以调用回调函数,我们已经不需要多做处理了,如果是异步情况,那么我们需要将每一个回调函数储存起来。
我们之前在then
方法中判断等待的时候,也将成功和失败的回调存储起来,但是每次只能存储一个,因此我们需要将存储的容器设为数组,通过数组的push
方法将回调函数存储起来。
const PENDING = "pending"; // 等待
const FULFILLED = "fulfilled"; // 成功
const REJECTED = "rejected"; // 失败
class MyPromise
constructor(executor) // 接收一个执行器
executor(this.resolve, this.reject); // 执行器会立即执行
status = PENDING; // 状态默认为pending等待
value = undefined; // 成功之后的值
reason = undefined; // 失败之后的原因
successCallback = []; // 使用数组存储成功的回调函数
failCallback = []; // 使用数组存储失败的回调函数
resolve = value => // value是成功之后的值
if (this.status !== PENDING) return; // 当状态不是等待,直接返回
this.status = FULFILLED; // 将状态改为成功
this.value = value; // 将成功的值传递
this.successCallback && this.successCallback(this.value); // 如果成功回调存在,则调用
reject = reason => // reason是失败之后的原因
if (this.status !== PENDING) return; // 当状态不是等待,直接返回
this.status = REJECTED; // 将状态改为失败
this.reason = reason; // 将失败的值传递
this.failCallback && this.failCallback(this.reason); // 如果失败回调存在,则调用
then(successCallback, failCallback) // then方法有两个参数
if (this.status === FULFILLED) // 成功调用第一个回调函数
successCallback(this.value);
else if (this.status === REJECTED) // 失败调用第二个回调函数
failCallback(this.reason);
else // 当状态为等待时,将成功回调和失败回调存储起来
this.successCallback.push(successCallback);
this.failCallback.push(failCallback);
更改成数组之后,那么我们原来在resolve
和reject
函数中调用成功或者失败的回调函数就不可以使用了,而是在其中循环调用数组中的回调函数。
const PENDING = "pending"; // 等待
const FULFILLED = "fulfilled"; // 成功
const REJECTED = "rejected"; // 失败
class MyPromise
constructor(executor) // 接收一个执行器
executor(this.resolve, this.reject); // 执行器会立即执行
status = PENDING; // 状态默认为pending等待
value = undefined; // 成功之后的值
reason = undefined; // 失败之后的原因
successCallback = []; // 成功的回调函数
failCallback = []; // 失败的回调函数
resolve = value => // value是成功之后的值
if (this.status !== PENDING) return; // 当状态不是等待,直接返回
this.status = FULFILLED; // 将状态改为成功
this.value = value; // 将成功的值传递
while (this.successCallback.length) // 循环执行数组中的回调函数
this.successCallback.shift()(this.value); // 调用回调函数
reject = reason => // reason是失败之后的原因
if (this.status !== PENDING) return; // 当状态不是等待,直接返回
this.status = REJECTED; // 将状态改为失败
this.reason = reason; // 将失败的值传递
while (this.failCallback.length) // 循环执行
this.failCallback.shift()(this.value); // 调用失败回调函数
then(successCallback, failCallback) // then方法有两个参数
if (this.status === FULFILLED) // 成功调用第一个回调函数
successCallback(this.value);
else if (this.status === REJECTED) // 失败调用第二个回调函数
failCallback(this.reason);
else // 当状态为等待时,将成功回调和失败回调存储起来
this.successCallback.push(successCallback);
this.failCallback.push(failCallback);
4 链式调用then方法
Promise的then
方法可以链式调用,下一个then
方法中成功回调函数的参数是上一个then
方法中的回调函数的返回值,也就是说,在下面代码中,value
的值为1。
let promise = new Promise((resolve, reject) => );
promise.then(() => return 1 )
.then(value => )
.then(() => )
我们首先来实现then
方法的链式调用。then
方法是Promise中的方法,如果要实现链式调用,那么每个then
方法都应该返回一个Promise对象,这样才可以调用。那么我们应该在then
方法中创建一个Promise对象,最后返回这个对象就可以。除此之外,我们还需要将then
方法中原来的代码传入到新创建对象的执行器中,保证调用方法后就立即执行。
then(successCallback, failCallbackvue结合Promise及async实现高效开发。