1. 手写 Promise 源码
实现 Promise 源码,首先要知道 Promise 的功能应该是什么样的
- Promise 是一个类,实例化的时候需要传递执行器,执行器会立即执行
- Promise 有三种状态,一旦确定就不可更改
- resolve 和 reject 函数式用来改变状态的
- then 方法被定义在原型对象中,判断状态调用相应函数
then 成功回调需要传递成功或失败的值
当有异步操作时,当前为等待状态,需要存储回调函数后续执行
- then 方法可以多次调用和链式调用
源码实现
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
class Promise {
// 每个Promise对象都有自己的状态和成功/失败的值,因此需要定义在实例对象上
status = PENDING;
value = undefined;
error = undefined;
successCallback = undefined;
failCallback = undefined;
constructor(executor) {
executor(this.resolve, this.reject);
}
// 不能定义为普通函数,否则resolve/reject自己调用自己,内部this为window或undefined;箭头函数会让此处this指向其实例对象
resolve = (value) => {
if (this.status !== PENDING) return;
this.status = FULFILLED;
// 保存成功的值
this.value = value;
};
reject = (error) => {
if (this.status !== PENDING) return;
this.status = REJECTED;
this.error = error;
};
then(successCallback, failCallback) {
if (this.status === FULFILLED) {
successCallback(this.value);
} else if (this.status === REJECTED) {
failCallback(this.error);
}
}
}
当实例化一个 Promise 对象的时候,其中有异步操作如耗时,ajax 等,不能立即 resolve.等待中的状态无法得知调用成功或失败回调
异步
需要给 pendding 状态的回调存储下来,并在 resolve 和 reject 下进行调用
resolve = (value) => {
if (this.status !== PENDING) return;
this.status = FULFILLED;
// 保存成功的值
this.value = value;
// 判断成功回调是否存在
this.successCallback && this.successCallback();
};
reject = (error) => {
if (this.status !== PENDING) return;
this.status = REJECTED;
this.error = error;
this.failCallback && this.failCallback();
};
then(successCallback, failCallback) {
if (this.status === FULFILLED) {
successCallback(this.value);
} else if (this.status === REJECTED) {
failCallback(this.error);
} else {
// 等待状态,需要存储回调函数后续执行
this.successCallback = successCallback;
this.failCallback = failCallback;
}
}
// 调用
var p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(100)
}, 200)
})
p.then(res => {
console.log(res)
})
p.then(res => {
console.log(res)
})
then 方法多次调用 当同一个 Promise 对象多次调 then 方法时(非链式调用),需要注意异步状态下,需要把传入的多个回调存储起来,按顺序调用
successCallback = []
failCallback = []
resolve = (value) => {
if (this.status !== PENDING) return;
this.status = FULFILLED;
// 保存成功的值
this.value = value;
// 判断成功回调是否存在
// this.successCallback && this.successCallback();
while (this.successCallback.length) {
this.successCallback.shift()(this.value)
}
};
reject = (error) => {
if (this.status !== PENDING) return;
this.status = REJECTED;
// 保存成功的值
this.error = error;
// 判断成功回调是否存在
// this.failCallbacj && this.failCallbacj();
while (this.failCallbacj.length) {
this.failCallbacj.shift()(this.error)
}
};
then (successCallback, failCallback) {
if(this.status === FULFILLED) {
successCallback(this.value)
} else if(this.status === REJECTED) {
failCallback(this.error)
} else { // 等待状态,需要存储回调函数后续执行
this.successCallback.push(successCallback)
this.failCallback.push(failCallback)
}
}
链式调用
then (successCallback, failCallback) {
let promise2 = new Promise((resolve, reject) => { // 返回一个全新的Promise
if(this.status === FULFILLED) {
let x = successCallback(this.value) // 前面then方法回调返回的值传递给下一个then方法回调中
resolve(x)
} else if(this.status === REJECTED) {
failCallback(this.error)
} else { // 等待状态,需要存储回调函数后续执行
this.successCallback.push(successCallback)
this.failCallback.push(failCallback)
}
})
return Promise2
}
判断 then 方法回调中返回的是普通值还是 Promise 对象
if (this.status === FULFILLED) {
let x = successCallback(this.value); // 前面then方法回调返回的值传递给下一个then方法回调中
parsePromise(x, resolve, reject); // ? 当返回的Promise中有异步操作怎么办?
}
function parsePromise(x, resolve, reject) {
if (x instanceof Promise) {
// x.then((value => resolve(value), error => reject(error))
x.then(resolve, reject);
} else {
resolve(x);
}
}
tips: 不能在 then 方法回调中返回当前 then 方法所返回的 Promise 对象,否则发生循环调用,捕捉此情况并把异常抛出来
如
var promise = new Promise((resolve, reject) => {
resolve(1);
});
var p1 = promise.then((value) => {
return p1; // 返回自身不被允许,错误会被捕获
});
解决方案
then (successCallback, failCallback) {
let promise2 = new Promise((resolve, reject) => { // 返回一个全新的Promise
if(this.status === FULFILLED) {
// 注意,还未实例化成功,此时是获取不到promise2的,包裹将以下代码包裹到setTimeout即可
let x = successCallback(this.value) // 前面then方法回调返回的值传递给下一个then方法回调中
parsePromise(promise2, x, resolve, reject)
} else if(this.status === REJECTED) {
failCallback(this.error)
} else { // 等待状态,需要存储回调函数后续执行
this.successCallback.push(successCallback)
this.failCallback.push(failCallback)
}
})
return Promise2
}
function parsePromise (promise2, x, resolve, reject) {
if(promise2 === x) {
return reject(new TypeError('Chaining cycle detedted for promise #<Promise>'))
}
if(x instanceof Promise) {
x.then(resolve, reject)
} else {
resolve(x)
}
}
1.1.1. 错误捕获
执行器中的错误
class Promise {
constructor(executor) {
try {
executor(this.resolve, this.reject);
} catch (error) {
this.reject(error);
}
}
}
then 方法回调函数中的错误
应该在下一个 then 方法的回调函数中捕获错误, 三种状态的错误都要进行处理
then (successCallback, failCallback) {
let promise2 = new Promise((resolve, reject) => { // 返回一个全新的Promise
if(this.status === FULFILLED) {
// 处理then方法中的错误
setTimeout(() => {
try {
let x = successCallback(this.value) // 前面then方法回调返回的值传递给下一个then方法回调中
parsePromise(promise2, x, resolve, reject)
} catch (error) {
reject(error) // 传递给下一个Promise对象回调中
}
}, 0);
} else if(this.status === REJECTED) {
setTimeout(() => {
try {
let x = failCallback(this.error) // 前面then方法回调返回的值传递给下一个then方法回调中
parsePromise(promise2, x, resolve, reject)
} catch (error) {
reject(error) // 传递给下一个Promise对象回调中
}
}, 0);
} else { // 等待状态,需要存储回调函数后续执行
// this.successCallback.push(successCallback) // 此时无法对错误情况进行处理,,需要改造
// this.failCallback.push(failCallback)
this.successCallback.push(() => {
setTimeout(() => {
try {
let x = successCallback(this.value) // 前面then方法回调返回的值传递给下一个then方法回调中
parsePromise(promise2, x, resolve, reject)
} catch (error) {
reject(error) // 传递给下一个Promise对象回调中
}
}, 0);
})
this.failCallback.push(() => {
setTimeout(() => {
try {
let x = failCallback(this.error) // 前面then方法回调返回的值传递给下一个then方法回调中
parsePromise(promise2, x, resolve, reject)
} catch (error) {
reject(error) // 传递给下一个Promise对象回调中
}
}, 0);
})
}
})
return Promise2
}
1.1.2. 将then方法参数变成可选参数
promise.then()等价于promise.then(value => value)
then (successCallback, failCallback) {
successCallback ? successCallback : value => value
failCallback ? failCallback : error => {throw error}
// ...
}
// 调用
promise.then().then().then(value => console.log(value))
1.1.3. Promise.all
function p1 () {
return new Promise(function (resolve, reject) {
setTimeout(function () {
resolve('p1')
}, 2000)
})
}
function p2 () {
return new Promise(function (resolve, reject) {
resolve('p2')
})
}
当p1,p2按顺序执行时,先得到p2结果.但是, 如果按顺序使用Promise.all方法中,一定是先得到p1的结果
Promise.all(['a', 'b', p1(), p2()]).then()
static all (array) {
let result = []
let index = 0
return new Promise((resolve, reject) => {
function addData (key, value) {
result[key] = value
index++
}
for (let i = 0;i < array.length;i++) {
let current = array[i]
if(current instanceof Promise) {
current.then((value) => {
addData(i, value)
}, (error) => {
reject(error)
})
} else {
addData(i, current)
}
}
if(index === array.length) {
resolve(result)
}
})
}
1.1.4. Promise.resolve
Promise.resolve()可以传入普通值或者Promise对象,都会创建一个Promise对象;如果传入的是一个Promise对象,那么该方法会原封不动的把这个Promise对象作为Promise.resolve()的返回值
static resolve (value) {
if(value instanceof Promsie) return value
return new Promise(resolve => resolve(value))
}
//调用
function p1 () {
return new Promise(function (resolve, reject) {
setTimeout(() => {
resolve('p1')
}, 1000);
})
}
function p2 () {
return new Promise(function (resolve, reject) {
resolve('p2')
})
}
Promise.resolve(100).then(value => console.log(value))
Promise.resolve(p1().then(value => console.log(value)))
1.1.5. Promise.prototype.finally
- 无论成功还是失败,最终都会执行一次
- 可以链式调用then方法拿到finally最终的结果,因此返回一个Promise对象
finally (callback) {
return this.then((value) => {
callback() // bug: 如果finally调用时返回了另外的Promise对象,此处没有等待其状态变更就立即返回了value,错误的
return value
}, (error) => {
callback()
throw error // 将错误结果传递到下一个then的失败回调中
})
}
p2().finally(() => {
console.log('finally')
return p1() // 注意,这个地方后面的then虽然是为了p1()返回的Promise注册的回调,但是then回调中的值不是p1,仍然是p2自身
}).then(value => {
console.log(value) // p2
})
因此
finally (callback) {
return this.then((value) => {
// 借用resolve静态方法包装为Promise对象
return Promise.resolve((callback()).then(newValue => value)
}, (error) => {
return Promise.resolve(callback()).then(() => throw error)
})
}
1.1.6. Promise.prototype.catch
catch (failCallback) {
return this.then(undefined, callback)
}
完整代码