1 MyPromise核心逻辑实现
-
promise是个构造函数
-
传入一个可执行函数。函数的入参第一个为 fullFill函数,第二个为 reject函数; 函数立即执行, 参数函数异步执行
-
状态一旦更改就不可以变更 只能 pending => fulfilled 或者 pending => rejected
-
then方法内部做的事情就判断状态,如果状态是成功,调用成功的回调函数,如果状态是失败,调用失败的函数
-
then成功回调有一个参数,表示成功之后的值,then失败回调有一个参数,表示失败后的原因
let p = new Promise((resolve, reject) => { resolve('成功'); // 状态改为成功 // reject('失败'); // 状态改为失败 }) p.then(value => { // 成功的值 console.log(value) // '成功' }, reason => { // 失败的原因 console.log(reason) // 失败 })
promise基本核心代码
const PENDING = 'pending'; const FULFILLED = 'fulfilled'; const REJECT = 'reject'; class MyPromise { constructor(executor) { // 立即执行 executor(this.resolve, this.reject); } status = PENDING; // 状态 value = undefined; // 成功的值 reason = undefined; // 失败的原因 resolve = (value) => { if(this.status !== PENDING) return; //状态改变过后不能再更改 this.status = FULFILLED; // 状态改为成功 this.value = value; // 保存成功的值 } reject = (reason) => { if(this.status !== PENDING) return; this.status = REJECT; // 状态改为失败 this.reason = reason; // 保存失败的原因 } then(successCallback, failCallback) { if(this.status === FULFILLED) { // 状态为成功,执行成功回调 successCallback(this.value); } else if(this.status === REJECT) { // 状态为失败,执行失败回调 failCallback(this.reason); } } }
2 在promise中加入异步逻辑
刚刚在实现promise时,是没有考虑异步情况的
let p = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('成功')
}, 2000)
})
p.then(value => {
// 成功的值
console.log(value) // '成功'
}, reason => {
// 失败的原因
console.log(reason) // 失败
})
异步代码不会阻塞主线程代码的执行,马上会执行then方法,但此时then中的回调函数都不会执行,因为两秒后才通过resolve将状态改为成功,所以此时promise的状态为‘pending’。为了在两2s后能执行promise中的异步代码,应该在then中加入异步逻辑处理,也就是在状态为’pending’时进行处理。
then方法中将成功回调和失败回调都存储起来,然后在resolve和reject中,分别执行存储的成功回调和异步回调
class MyPromise {
...
// 定义存储的回调
successCallback = undefined; // 成功回调
failCallback = undefined; // 失败回调
...
// then方法中将成功和失败回调存储起来
then(successCallback, failCallback) {
if(this.status === FULFILLED) { // 状态为成功,执行成功回调
successCallback(this.value);
} else if(this.status === REJECT) { // 状态为失败,执行失败回调
failCallback(this.reason);
} else {
// 当前状态为pending
// 将成功回调和失败回调存储起来
this.successCallback = successCallback;
this.failCallback = failCallback;
}
}
...
// resolve方法中执行存储的成功回调
resolve = (value) => {
...
this.successCallback && this.successCallback(this.value);
...
}
// reject方法中执行存储的失败回调
reject = (reason) => {
...
this.failCallback && this.failCallback(this.reason);
...
}
}
3 实现then方法多次调用添加多个处理函数
当promise中有异步任务时,有多个then的情况时,最后一个then会覆盖前面的then
let p = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('成功')
}, 2000)
})
p.then(value => {
console.log(value)
})
p.then(value => {
console.log(value)
})
p.then(value => {
console.log(value) // '成功'
})
// 打印一次 '成功'
只执行了一次console.log,这与实际promise不符,所以此时应该在状态为’pending’时将多个then的回调函数都存储起来,然后在resolve和reject方法中将存储的回调任务依次执行
class MyPromise {
...
// 定义存储的回调
successCallback = []; // 成功回调数组
failCallback = []; // 失败回调数组
...
// then方法中将成功和失败回调存储起来
then(successCallback, failCallback) {
if(this.status === FULFILLED) { // 状态为成功,执行成功回调
successCallback(this.value);
} else if(this.status === REJECT) { // 状态为失败,执行失败回调
failCallback(this.reason);
} else {
// 当前状态为pending
// 将所有成功回调和失败回调存储起来
this.successCallback.push(successCallback);
this.failCallback.push(failCallback);
}
}
...
// resolve方法中执行存储的成功回调
resolve = (value) => {
...
while(this.successCallback.length) {
this.successCallback.shift()(this.value);
}
...
}
// reject方法中执行存储的失败回调
reject = (reason) => {
...
while(this.failCallback.length) {
this.failCallback.shift()(this.reason);
}
...
}
}
此时有多个then的情况时,每一个then中的回调函数都会被正确执行
let p = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('成功')
}, 2000)
})
p.then(value => {
console.log(value)
})
p.then(value => {
console.log(value)
})
p.then(value => {
console.log(value) // '成功'
})
// 打印三次 '成功'
4 实现then方法的链式调用
promise/A+规范规定,then方法可以链式调用
let p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('成功')
}, 2000)
})
p.then(value => {
console.log(value); // 成功
return 100;
}).then(value => {
console.log(value); // 100
})
实现链式调用,可以在then方法中创建promise对象,然后返回这个promise对象
then(successCallback, failCallback) {
let promise1 = new MyPromise(() => {
if(this.status === FULFILLED) { // 状态为成功,执行成功回调
successCallback(this.value);
} else if(this.status === REJECT) { // 状态为失败,执行失败回调
failCallback(this.reason);
} else {
// 当前状态为pending
// 将成功回调和失败回调存储起来
this.successCallback.push(successCallback);
this.failCallback.push(failCallback);
}
});
return promise1;
}
如何把上一个then方法中回调函数的返回值传递给下一个then方法中的回调函数,可以将回调函数执行的返回值,通过新创建的Promise的resolve传递给下一个then
then(successCallback, failCallback) {
let promise1 = new MyPromise((resolve, reject) => {
if(this.status === FULFILLED) { // 状态为成功,执行成功回调
let x = successCallback(this.value);
resolve(x);
} else if(this.status === REJECT) { // 状态为失败,执行失败回调
failCallback(this.reason);
} else {
// 当前状态为pending
// 将成功回调和失败回调存储起来
this.successCallback.push(successCallback);
this.failCallback.push(failCallback);
}
});
return promise1;
}
promise/A+规范规定,then方法可以返回一个普通值,也可以返回一个新的promise对象
let p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('成功')
}, 2000)
})
p.then(value => {
console.log(value); // 成功
return 100; // 返回普通值
}).then(value => {
// 返回一个Promise对象
return new Promise(resolve => {
resolve(200)
})
}).then(value => {
})
如果返回的是一个普通值,可以像前面处理一样,直接resolve这个普通值;如果返回的是一个Promise对象,就需要查看这个对象的状态,根据Promise对象返回的结果决定调用resolve还是调用reject
由于在多个地方需要判断这个过程,所有封装成一个函数
class MyPromise {
...
then(successCallback, failCallback) {
let promise1 = new MyPromise((resolve, reject) => {
if(this.status === FULFILLED) { // 状态为成功,执行成功回调
let x = successCallback(this.value);
// 判断x的值是普通值还是promise对象
// 如果是普通值,直接调用resolve
// 如果是promise对象,查看promise对象返回的结果
// 再根据promise对象返回的结果,决定调用resolve还是调用reject
resolvePromise(x, resolve, reject) // 解析返回的结果
} else if(this.status === REJECT) { // 状态为失败,执行失败回调
failCallback(this.reason);
} else {
// 当前状态为pending
// 将成功回调和失败回调存储起来
this.successCallback.push(successCallback);
this.failCallback.push(failCallback);
}
});
return promise1;
}
...
}
function resolvePromise(x, resolve, reject) {
if(x instanceof MyPromise) {
// promise对象
x.then(resolve, reject);
} else {
// 普通值
resolve(x);
}
}
then方法的回调函数可以返回promise对象,但是有一种是例外的,在then方法的回调函数中不能返回当前的then方法它所返回的promise对象,否则会发生promise对象的循环调用,程序会报错
let promise = new Promise(resolve => {
resolve(100)
});
let p1 = promise.then(value => {
console.log(value)
return p1;
})
此时会报错: Uncaught (in promise) TypeError: Chaining cycle detected for promise #
let promise = new Promise(resolve => {
resolve(100)
});
let p1 = promise.then(value => {
console.log(value)
return p1;
})
p1.then(()=>{}, (reason)=>{
console.log(reason.message)
})
此时promise不会报错,会拿到这个错误信息
所以我们也需要判断出自己返回自己的情况。如果自己返回自己,就走reject
resolvePromise函数传递参数时,把返回的promise对象也传进来进行判断
class MyPromise {
...
then(successCallback, failCallback) {
let promise1 = new MyPromise((resolve, reject) => {
if(this.status === FULFILLED) { // 状态为成功,执行成功回调
queueMicrotask(()=>{
let x = successCallback(this.value);
// 判断x的值是普通值还是promise对象
// 如果是普通值,直接调用resolve
// 如果是promise对象,查看promise对象返回的结果
// 再根据promise对象返回的结果,决定调用resolve还是调用reject
resolvePromise(promise1, x, resolve, reject) // 解析返回的结果
})
} else if(this.status === REJECT) { // 状态为失败,执行失败回调
queueMicrotask(()=>{
let x = failCallback(this.reason);
resolvePromise(promise1, x, resolve, reject) // 解析返回的结果
})
} else {
// 当前状态为pending
// 将成功回调和失败回调存储起来
this.successCallback.push(successCallback);
this.failCallback.push(failCallback);
}
});
return promise1;
}
...
}
function resolvePromise(promise1, x, resolve, reject) {
if(promise1 === x) return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
if(x instanceof MyPromise) {
// promise对象
x.then(resolve, reject);
} else {
// 普通值
resolve(x);
}
}
敲黑板:这里需要注意,调用resolvePromise函数时需要传递promise对象promise1,但是此时MyPromise还没有实例完成,拿不到promise1,此时怎么办呢?将内部的处理逻辑放到异步队列中,等MyPromise实例化完成后,再执行内部的代码,此时promise1对象就能成功获取。
let promise = new MyPromise(resolve => {
resolve(100)
});
let p1 = promise.then(value => {
console.log(value)
return p1;
})
p1.then(()=>{}, (reason)=>{
console.log(reason.message)
})
5 捕获错误
promise中有错误时,会传递给then方法的第二个回调函数
let promise = new Promise((resolve, reject) => {
throw new Error('错误信息')
}).then(()=>{}, (reason) => {
console.log(reason) // 错误信息
})
-
在执行器里捕获错误
class MyPromise { constructor(executor) { // 立即执行 try { executor(this.resolve, this.reject); } catch(e) { this.reject(e); } } ... }
-
在then中捕获错误,状态为成功,失败,等待中时都应该处理
then(successCallback, failCallback) {
let promise1 = new MyPromise((resolve, reject) => {
if(this.status === FULFILLED) { // 状态为成功,执行成功回调
queueMicrotask(()=>{
try {
let x = successCallback(this.value);
// 判断x的值是普通值还是promise对象
// 如果是普通值,直接调用resolve
// 如果是promise对象,查看promise对象返回的结果
// 再根据promise对象返回的结果,决定调用resolve还是调用reject
resolvePromise(promise1, x, resolve, reject) // 解析返回的结果
} catch(e) {
reject(e);
}
})
} else if(this.status === REJECT) { // 状态为失败,执行失败回调
queueMicrotask(() => {
try {
let x = failCallback(this.reason);
resolvePromise(promise1, x, resolve, reject)
} catch(e) {
reject(e);
}
})
} else {
// 当前状态为pending
// 将成功回调和失败回调存储起来
this.successCallback.push(() => {
queueMicrotask(() => {
try {
let x = successCallback(this.value);
resolvePromise(promise1, x, resolve, reject)
} catch(e) {
reject(e);
}
})
});
this.failCallback.push(() => {
queueMicrotask(() => {
try {
let x = failCallback(this.reason);
resolvePromise(promise1, x, resolve, reject)
} catch(e) {
reject(e);
}
})
});
}
});
return promise1;
}
至此,promise的核心逻辑基本上已经完成了
6 将then方法的参数变成可选参数
promise/A+规范中,then方法没有传任何参数的话,等同于默认传递前面then或promise中的状态
let promise = new Promise((resolve, reject) => {
resolve(100);
});
promise.then().then().then(value => console.log(value))
// 等同于
let promise = new Promise((resolve, reject) => {
resolve(100);
});
promise.then(value => value)
.then(value => value)
.then(value => console.log(value))
所以在then方法内部要对参数先进行判断是否存在
successCallback ? successCallback : value => value;
failCallback ? failCallback : reason => { throw reason };
7 Promise.all方法实现
function p1() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('p1')
}, 2000)
})
}
function p2() {
return new Promise((resolve, reject) => {
resolve('p2')
})
}
Promise.all('a', 'b', p1(), p2(), 'c').then(result => {
// result => ['a', 'b', 'p1', 'p2', 'c']
})
可以看出,Promise.all接受一个数组参数,数组元素可以是普通值,也可以是promise对象,Promise.all返回一个promise对象,成功状态传递过来的值是参数数组resolve的顺序结果数组,实现如下:
static all(array) {
let result = []; // 结果数组
// 添加数据的公共方法
function addData(key, value) {
result[key] = value;
}
// 返回MyPromise对象
return new MyPromise((resolve, reject) => {
for(let i = 0; i < array.length; i++) {
let current = array[i];
if(current instanceof MyPromise) {
// MyPromise对象,成功的值添加到结果数组中,失败的话,调用reject
current.then((value) => {addData(i, value)}, (reason) = {reject(reason))}
} else {
// 普通值
addData(i, array[i]);
}
}
resolve(result)
})
}
测试一下:
function p1() {
return new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('p1')
}, 2000)
})
}
function p2() {
return new MyPromise((resolve, reject) => {
resolve('p2')
})
}
MyPromise.all('a', 'b', p1(), p2(), 'c').then(result => {
// result => [ 'a', 'b', <1 empty item>, 'p2', 'c' ]
})
原因,p1中有异步代码,在all中for循环时,遍历到p1时,由于p1是MyPromise对象,且里面有异步代码,此时状态为‘pending’,所以then中的代码不会执行,即不会将结果添加到结果数组
解决办法,每添加一次数据,就计数一次,该计数值和数组长度相等时,就可以resolve了
static all(array) {
let result = []; // 结果数组
let index = 0;
// 返回MyPromise对象
return new MyPromise((resolve, reject) => {
// 添加数据的公共方法
function addData(key, value) {
result[key] = value;
index++;
if(index === array.length) {
resolve(result);
}
}
for(let i = 0; i < array.length; i++) {
let current = array[i];
if(current instanceof MyPromise) {
// MyPromise对象,成功的值添加到结果数组中,失败的话,调用reject
current.then((value) => {addData(i, value)}, (reason) => {reject(reason)})
} else {
// 普通值
addData(i, array[i]);
}
}
// resolve(result)
})
}
8 Promise.resolve
将给定的值转换成promise对象
Promise.resolve(10).then(value => {console.log(value)})
Promise.resolve(p1).then(value => {console.log(value)})
resolve方法参数如果是promise对象,直接返回该对象,如果不是promise对象,将该值包裹成promise对象然后返回
static resolve(value) {
if(value instanceof MyPromise) return value;
return new MyPromise(resolve => resolve(value))
}
9 finally方法
特性:
- 无论当前promise状态是成功还是失败,finally方法都会被执行
- finally方法仍然可以链式调用then方法
finally(callback) {
return this.then((value) => {
callback();
return value;
}, (reason) => {
callback();
throw reason;
})
}
测试:
function p2() {
return new MyPromise((resolve, reject) => {
resolve('p2')
// reject('p2')
})
}
p2().finally(() => {
console.log('finally')
return 'p2'
}).then(value => {
console.log(value)
}, reason => {
console.log(reason)
})
此时可以看出,无论promise状态是成功还是失败,都会执行finally方法
但是,会有一个问题,
function p1() {
return new MyPromise((resolve, reject) => {
setTimeout(()=> {
resolve('p1')
}, 2000)
})
}
function p2() {
return new MyPromise((resolve, reject) => {
resolve('p2')
})
}
p2().finally(() => {
console.log('finally')
return p1();
}).then(value => {
console.log(value)
}, reason => {
console.log(reason)
})
then中的方法应该等p1计时器完成后再执行,但是这里会立即执行,所以改变如下:
finally(callback) {
return this.then((value) => {
return MyPromise.resolve(callback()).then(() => value);
}, (reason) => {
return MyPromise.resolve(callback()).then(() => {throw reason});
})
}
10 catch方法
catch(failCallback) {
return this.then(undefined, failCallback);
}
最后,整个手写过程如下:
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECT = 'reject';
class MyPromise {
constructor(executor) {
// 立即执行
try {
executor(this.resolve, this.reject);
} catch(e) {
this.reject(e);
}
}
status = PENDING; // 状态
value = undefined; // 成功的值
reason = undefined; // 失败的原因
successCallback = []; // 成功回调
failCallback = []; // 失败回调
resolve = (value) => {
if(this.status !== PENDING) return; //状态改变过后不能再更改
this.status = FULFILLED; // 状态改为成功
this.value = value; // 保存成功的值
// this.successCallback && this.successCallback(this.value);
while(this.successCallback.length) {
this.successCallback.shift()();
}
}
reject = (reason) => {
if(this.status !== PENDING) return;
this.status = REJECT; // 状态改为失败
this.reason = reason; // 保存失败的原因
// this.failCallback && this.failCallback(this.reason);
while(this.failCallback.length) {
this.failCallback.shift()();
}
}
then(successCallback, failCallback) {
successCallback ? successCallback : value => value;
failCallback ? failCallback : reason => { throw reason };
let promise1 = new MyPromise((resolve, reject) => {
if(this.status === FULFILLED) { // 状态为成功,执行成功回调
queueMicrotask(()=>{
try {
let x = successCallback(this.value);
// 判断x的值是普通值还是promise对象
// 如果是普通值,直接调用resolve
// 如果是promise对象,查看promise对象返回的结果
// 再根据promise对象返回的结果,决定调用resolve还是调用reject
resolvePromise(promise1, x, resolve, reject) // 解析返回的结果
} catch(e) {
reject(e);
}
})
} else if(this.status === REJECT) { // 状态为失败,执行失败回调
queueMicrotask(() => {
try {
let x = failCallback(this.reason);
resolvePromise(promise1, x, resolve, reject)
} catch(e) {
reject(e);
}
})
} else {
// 当前状态为pending
// 将成功回调和失败回调存储起来
this.successCallback.push(() => {
queueMicrotask(() => {
try {
let x = successCallback(this.value);
resolvePromise(promise1, x, resolve, reject)
} catch(e) {
reject(e);
}
})
});
this.failCallback.push(() => {
queueMicrotask(() => {
try {
let x = failCallback(this.reason);
resolvePromise(promise1, x, resolve, reject)
} catch(e) {
reject(e);
}
})
});
return;
}
});
return promise1;
}
finally(callback) {
return this.then((value) => {
return MyPromise.resolve(callback()).then(() => value);
}, (reason) => {
return MyPromise.resolve(callback()).then(() => {throw reason});
})
}
catch(failCallback) {
return this.then(undefined, failCallback);
}
static all(array) {
let result = []; // 结果数组
let index = 0;
// 返回MyPromise对象
return new MyPromise((resolve, reject) => {
// 添加数据的公共方法
function addData(key, value) {
result[key] = value;
index++;
if(index === array.length) {
resolve(result);
}
}
for(let i = 0; i < array.length; i++) {
let current = array[i];
if(current instanceof MyPromise) {
// MyPromise对象,成功的值添加到结果数组中,失败的话,调用reject
current.then((value) => {addData(i, value)}, (reason) => {reject(reason)})
} else {
// 普通值
addData(i, array[i]);
}
}
// resolve(result)
})
}
static resolve(value) {
if(value instanceof MyPromise) return value;
return new MyPromise(resolve => resolve(value))
}
}
function resolvePromise(promise1, x, resolve, reject) {
if(promise1 === x) {
// 如果自己返回自己,就报错,并且return后,不用判断后面的逻辑
return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
}
if(x instanceof MyPromise) {
// promise对象
x.then(resolve, reject);
} else {
// 普通值
resolve(x);
}
}
以上是主要的核心逻辑,还有些边界情况可能没有考虑