章
目
录
Promise是js处理异步操作的重要工具,它有效解决了异步回调地狱的问题。通过Promise,我们能够更清晰、简洁地处理异步任务,实现代码的链式调用。下面,我们将逐步深入探讨Promise及其API源码的实现思考过程。
一、Promise与then方法的实现
Promise有三个状态:pending
(进行中)、fulfilled
(已完成)和rejected
(已失败),并且状态一旦改变,就不会再变动。基于这些特性,我们来实现Promise的基本功能。
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED ='rejected';
class myPromise {
constructor(executor) {
this.status = PENDING;
this.value = undefined;
this.reason = undefined;
// 存储then方法中成功回调函数的数组,初始注释掉,后续根据需求启用
// this.onFulfilledCbs = [];
// 存储then方法中失败回调函数的数组,初始注释掉,后续根据需求启用
// this.onRejectedCbs = [];
const resolve = (value) => {
if (this.status === PENDING) {
this.status = FULFILLED;
this.value = value;
// 当状态变为已完成时,执行存储的成功回调函数,初始注释掉,后续根据需求启用
// this.onFulfilledCbs.forEach(cb => cb(value));
}
};
const reject = (reason) => {
if (this.status === PENDING) {
this.status = REJECTED;
this.reason = reason;
// 当状态变为已失败时,执行存储的失败回调函数,初始注释掉,后续根据需求启用
// this.onRejectedCbs.forEach(cb => cb(reason));
}
};
// 执行传入的executor函数,并传递resolve和reject函数
executor(resolve, reject);
}
then(onFulfilled, onRejected) {
return new myPromise((resolve, reject) => {
let result = undefined;
if (this.status === FULFILLED) {
result = onFulfilled(this.value);
resolve(result);
}
if (this.status === REJECTED) {
result = onRejected(this.reason);
reject(result);
}
// 如果当前状态还是pending,说明异步操作还未完成,需要存储回调函数,初始注释掉,后续根据需求启用
// if (this.status === PENDING) {
// this.onFulfilledCbs.push(onFulfilled);
// this.onRejectedCbs.push(onRejected);
// }
});
}
}
// 测试示例
new myPromise((resolve, reject) => {
// setTimeout(() => {
resolve(111);
// }, 1000);
}).then(res => {
console.log('res', res);
});
上述代码初步实现了Promise的链式调用。不过,resolve
接收的参数实际上有4种情况,目前代码只处理了基础值这一种。resolve
的参数还可能是promise
、thenable
对象或者空
,并且resolve
出的值不能是promise
自身,否则会导致无限循环引用。为了兼容这些情况,我们进一步完善代码,并使用setTimeout
模拟异步操作:
// promise有三种状态,分别是pending、FULFILLED 、rejected,pending是初始状态,FULFILLED 是成功状态,rejected是失败状态
// 状态一旦改变,不会再变
const PENDING = "pending";
const FULFILLED = "FULFILLED ";
const REJECTED = "rejected";
class Promise1 {
constructor(excutor) {
// 实际上resolve,reject这两个函数不是外部传来的,外部只是调用内部的方法并传入值
this.status = PENDING;
this.value = undefined;
this.error = undefined;
// 存储then方法中onFULFILLED的回调函数
this.onFulfilledCallbacks = [];
// 存储then方法中onRejected的回调函数
this.onRejectedCallbacks = [];
const resolve = (value) => {
if (this.status === PENDING) {
this.status = FULFILLED;
this.value = value;
this.onFulfilledCallbacks.forEach((callback) => callback());
}
};
const reject = (error) => {
if (this.status === PENDING) {
this.status = REJECTED;
this.error = error;
this.onRejectedCallbacks.forEach((callback) => callback());
}
};
try {
excutor(resolve, reject);
} catch (error) {
reject(error);
}
}
then(onFulfilled, onRejected) {
// 处理onFULFILLED不是函数的情况,如果不是函数,就返回传入的值
onFulfilled = typeof onFulfilled === "function"? onFulfilled : (value) => value;
// 处理onRejected不是函数的情况,如果不是函数,就抛出错误
onRejected = typeof onRejected === "function"? onRejected : (error) => {
throw error;
};
// then最终还是会return一个promise
const promise = new Promise((resolve, reject) => {
const handleFulfilled = () => {
try {
const result = onFulfilled(this.value);
resolvePromise(promise, result, resolve, reject);
} catch (error) {
reject(error);
}
};
const handleRejected = () => {
try {
const result = onRejected(this.error);
resolvePromise(promise, result, resolve, reject);
} catch (error) {
reject(error);
}
};
if (this.status === FULFILLED) {
// 这里使用setTimeout模拟promise是异步的
setTimeout(handleFulfilled, 0);
} else if (this.status === REJECTED) {
setTimeout(handleRejected, 0);
} else {
this.onFulfilledCallbacks.push(() => setTimeout(handleFulfilled, 0));
this.onRejectedCallbacks.push(() => setTimeout(handleRejected, 0));
}
});
return promise;
}
}
function resolvePromise(promise, result, resolve, reject) {
// 如果promise和result是同一个对象,则抛出TypeError
if (promise === result) {
return reject(new TypeError("Chaining cycle detected for promise"));
}
// 如果result是thenable对象
if (result && (typeof result === "object" || typeof result === "function")) {
try {
const then = result.then;
if (typeof then === "function") {
// then就是接收两个参数:resolve或reject,然后出结果的函数,直接执行它,但注意this指向,并传参
then.call(
this,
(value) => {
resolvePromise(promise, value, resolve, reject);
},
(reason) => reject(reason)
);
} else {
resolve(result);
}
} catch (error) {
reject(error);
}
} else {
// 否则直接调用resolve
resolve(result);
}
}
在这段代码中,resolvePromise
函数用于处理resolve
参数的各种情况,确保代码的健壮性。
二、catch方法的实现
catch
方法用于捕获Promise中的错误,它实际上等同于then
方法中的第二个参数。因此,我们可以通过调用then
方法,并将catch
中的回调函数传递给then
的第二个参数来实现catch
方法:
// 声明在Promise类中
catch(cb) {
return this.then(_, cb);
}
三、finally方法的实现
finally
方法无论Promise成功还是失败都会执行。我们可以利用then
方法来实现finally
,无论成功或失败,都通过resolve
返回一个promise
,这样catch
后面也能继续进行链式调用:
// 声明在Promise类中
finally(cb) {
return this.then(
value => Promise1.resolve(cb()).then(() => value),
reason => Promise1.resolve(cb()).then(() => reason)
);
}
四、Promise类方法的实现
(一)resolve方法
Promise.resolve(value)
等同于 new Promise((resolve) => { resolve(value) })
。在实现时,我们需要进行判断,如果传入的值本身就是Promise
实例,直接返回该实例;否则,创建并返回一个新的Promise
实例:
static resolve(value) {
if (value instanceof Promise1) {
return value;
}
return new Promise1(() => resolve(value));
}
(二)reject方法
reject
方法与resolve
方法类似,Promise.reject('err')
等同于 new Promise1((_, reject) => reject('err'))
,实现代码如下:
static reject(reason) {
return new Promise1((_, reject) => reject(reason));
}
(三)race方法
race
方法用于处理多个Promise
实例,当其中一个Promise
最先执行完毕时,整个操作的结果就是这个最先完成的Promise
的状态。它接收一个数组作为参数,我们遍历这个数组,通过Promise.resolve
包装每个Promise
实例,并在then
回调中处理结果:
static race(arr) {
return new Promise1((resolve, reject) => {
arr.forEach(pro => {
Promise1.resolve(pro).then((value) => {
resolve(value);
}, reason => {
reject(reason);
});
});
});
}
(四)all方法
all
方法要求所有Promise
实例都成功,整个操作才成功;只要有一个失败,结果就是失败。实现时,我们需要计算成功的次数,当成功的个数等于数组长度时,resolve
结果数组:
static all(arr) {
return new Promise1((resolve, reject) => {
let res = [];
let completeCount = 0;
if (arr.length === 0) {
resolve(res);
return;
}
arr.forEach((pro) => {
Promise1.resolve(pro).then(
(value) => {
res[completeCount] = value;
completeCount++;
if (completeCount === arr.length) {
resolve(res);
}
},
(err) => {
reject(err);
}
);
});
});
}
(五)allSettled方法
allSettled
方法不管Promise
实例是成功还是失败,都会返回结果。结果数组中的每个对象包含status
(表示状态,fulfilled
为成功,rejected
为失败)和对应的结果值。当所有Promise
实例都执行完毕后,resolve
结果数组:
static allSettled(promises) {
return new Promise1((resolve, reject) => {
const results = [];
let completedCount = 0;
if (promises.length === 0) {
resolve(results);
return;
}
promises.forEach((promise, index) => {
Promise1.resolve(promise).then(
(value) => {
results[index] = {
status: "fulfilled",
value,
};
completedCount++;
if (completedCount === promises.length) {
resolve(results);
}
},
(reason) => {
results[index] = {
status: "rejected",
reason,
};
completedCount++;
if (completedCount === promises.length) {
resolve(results);
}
}
);
});
});
}
最后,给大家留一个作业:实现any
方法。这个方法要求只要有一个Promise
实例成功,包装实例就返回fulfilled
状态;只有当所有Promise
实例都失败时,结果才是rejected
状态。希望大家通过实践,进一步加深对Promise的理解。