# 手写 promise
# 完整代码
const PENDING = Symbol('pending'); | |
const FULFILL = Symbol('resolve'); | |
const REJECT = Symbol('reject'); | |
class myPromise { | |
constructor(executor) { | |
/* 初始化 promise pending 状态 */ | |
this.status = PENDING; | |
/* 记录当前 promise 兑现值和拒因 */ | |
this.value = undefined; | |
this.reason = undefined; | |
/* | |
记录当前 promise 的 fulfilled 与 rejected 回调 | |
使用数组是因为一个 promise 可以接受多次 then,catch 回调,否则只需各存储一个函数即可 | |
*/ | |
this.onFulFilledFns = []; | |
this.onRejectedFns = []; | |
const resolve = (value) => { | |
/* | |
此处存在两次判断 promise 状态,分别是执行 executor 和执行微任务时 | |
保证 promise 状态一经确定不再改变 | |
*/ | |
if (this.status === PENDING) { | |
/* resolve 方法处理 promise 和 thenable 特殊参数 */ | |
if ( | |
value instanceof myPromise || | |
(typeof value === 'object' && typeof value.then === 'function') | |
) { | |
return value.then(resolve, reject); | |
} | |
/* 普通参数时 */ | |
this.status = FULFILL; | |
this.value = value; | |
/* 将 fulfilled 回调拿出来执行(这些回调实际上是把回调注册为微任务) */ | |
this.onFulFilledFns.forEach((fn) => { | |
fn(); | |
}); | |
} | |
}; | |
const reject = (reason) => { | |
if (this.status === PENDING) { | |
this.status = REJECT; | |
this.reason = reason; | |
this.onRejectedFns.forEach((fn) => { | |
fn(this.reason); | |
}); | |
} | |
}; | |
/* 此处捕获 executor 执行中抛出的错误,若存在错误则 promise 变成拒绝状态 */ | |
try { | |
executor(resolve, reject); | |
} catch (err) { | |
reject(err); | |
} | |
} | |
then(onFulFilledFn, onRejectedFn) { | |
/* 根据 Promise A + 标准,若回调不为函数类型则忽视,默认值回调会将状态继续传入下一个 promise 中 */ | |
onFulFilledFn = typeof onFulFilledFn === 'function' ? onFulFilledFn : (res) => res; | |
onRejectedFn = | |
typeof onRejectedFn === 'function' | |
? onRejectedFn | |
: (err) => { | |
throw err; | |
}; | |
/* then 方法返回值是一个新 promise */ | |
return new myPromise((resolve, reject) => { | |
/* 若捕获当前 promise 已经改变状态,则直接将其添入微任务队列 */ | |
if (this.status === FULFILL) { | |
queueMicrotask(() => { | |
/* 捕获 fulfilled 回调错误,捕获到则改变新 promise 状态为拒绝状态 */ | |
try { | |
let result = onFulFilledFn(this.value); | |
/* 返回值传递给下一个 fulfilled 回调 */ | |
resolve(result); | |
} catch (err) { | |
reject(err); | |
} | |
}); | |
} | |
if (this.status === REJECT) { | |
queueMicrotask(() => { | |
try { | |
let result = onRejectedFn(this.reason); | |
/* 返回值传递给下一个 fulfilled 回调 */ | |
resolve(result); | |
} catch (err) { | |
reject(err); | |
} | |
}); | |
} | |
/* 若当前 promise 没改变状态 / 改变状态的异步还未执行,先储存回调 */ | |
if (this.status === PENDING) { | |
this.onFulFilledFns.push(() => { | |
queueMicrotask(() => { | |
try { | |
let result = onFulFilledFn(this.value); | |
resolve(result); | |
} catch (err) { | |
reject(err); | |
} | |
}); | |
}); | |
this.onRejectedFns.push(() => { | |
queueMicrotask(() => { | |
try { | |
let result = onRejectedFn(this.reason); | |
resolve(result); | |
} catch (err) { | |
reject(err); | |
} | |
}); | |
}); | |
} | |
}); | |
} | |
catch(onRejectedFn) { | |
/* catch 方法类似 then 方法的语法糖 */ | |
return this.then(undefined, onRejectedFn); | |
} | |
finally(onFinallyFn) { | |
/* finally 无任何影响,仅单独调用,且无任何参数 */ | |
return this.then( | |
(res) => { | |
onFinallyFn(); | |
return res; | |
}, | |
(err) => { | |
onFinallyFn(); | |
throw err; | |
} | |
); | |
} | |
} |
# 构造函数 constructor 讲解
初始化相关属性
初始化状态 status
this.status = PENDING
将状态初始化为PENDING
保存 promise 成功时的要传递的值 value 和失败时的原因 reason
this.value = undefined
,this.reason = undefind
初始化value
andreason
用来保存成功时要传递的值,和失败时要传递的原因
设计 onFullfilledFns, onRejectedFns 两个数组
this.onFullfilledFns = []
,this,onRejectedFns = []
,初始化onFullfilledFns
,onRejectedFns
来存储成功时和失败时将要注册为微任务的callback
。
设计 resolve 函数
判断当前 promise 的状态
if (this.status === PENDING)
判断当前 promise 的状态是否为PENDING
- 是的话,则进行接下来的操做
- 不是的话,则什么都不做(因为 promise 的状态一旦确定就不能再更改。)
判断 value 是否为 promise or thenabale
if (value instanceof myPromise || typeof value.then === 'function')
判断 value 是否为 promise or thenable。- 是的话,
return value.then(resolve, reject)
则调用 value 的then
方法并将resolve
和reject
作为参数传递, 并结束 resolve 函数的调用,不再进行接下来的操作。这样当 value 的状态发生改变时就会触发resolve
orreject
来修改现在这个 promise 的状态。(因为当resolve
的value
为 promise or thenable 的时候,当前 promise 的状态将随 value 的状态改变而改变,并保持一致)。 - 不是的话,则进行接下来的操作
进行 promise 状态的修改,并遍历执行
onFulFilledFn
来将对应的callback
注册为微任务this.status === PENDING
判断当前 promise 的状态是否为 PENDING,是的话进行接下来的操作,不是的话,则不做接下来的操作(这样做也是为了保证 promise 的状态一旦确定就不再改变)this.status = FULLFILL
,this.value = value
, 将 promise 的状态修改为成功,并将成功的值 value 赋值给 promise.value。this.onFullfilledFns.forEach((fn) => fn())
遍历onFullfilledFns
,将其中存储的将对应callback
注册为微任务的函数全部调用执行
设计 rejeted 函数
判断当前 promise 的状态
if (this.status === PENDING)
判断当前 promise 的状态是否为PENDING
- 是的话,则进行接下来的操作
- 不是的话,则什么都不做(因为 promise 的状态一旦确定就不能再更改。)
进行 promise 状态的修改,并遍历执行
onRejectedFn
来将对应的callback
注册为微任务this.status === PENDING
判断当前 promise 的状态是否为 PENDING,是的话进行接下来的操作,不是的话,则不做接下来的操作(这样做也是为了保证 promise 的状态一旦确定就不再改变)this.status = REJECT
,this.reason = reason
, 将 promise 的状态修改为失败,并将成功的值 reason 赋值给 promise.reason。this.onRejectedFns.forEach((fn) => fn(this.reason))
遍历onRejectedFns
,将其中存储的将对应callback
注册为微任务的函数全部调用执行
以 try-catch 的形式调用传递来的 executor 函数,并捕获错误
- 在 try 里面调用
executor
,并将resolve
和reject
作为参数传递 - 在 catch 里面捕获错误,一点捕获到错误,立刻调用
reject
(这样做是因为 executor 执行器函数在执行的时候,一旦发生错误,promise 的状态将马上变成失败)
- 在 try 里面调用
# then 方法讲解
- 判断 onFullfilledFn, onRejectedFn 类型
onFullfilledFn = typeof onFullfilled === 'function ? onFullfilled : (res) => res
判断onFullfilledFn
是否为一个函数,当不为函数时,则默认将其赋值为 (res) => res (这样做时因为根据 Promise A + 标准,若回调不为函数类型则忽视,默认值回调会将状态继续传入下一个 promise 中)。onRejectedFn
的处理同上
new 一个 promise 返回,并在 executor 中做如下操作
判断调用 then 方法的 promise 状态
状态为
FULLFILL
时- 将接下来的操作注册为微任务
- 将接下来的操作放到 try-catch 中
- 在 try 中调用
onFullfilledFn
并将调用then
方法的 promise 的value
作为参数传递,将返回值赋值给result
, 调用resolve
并将result
作为参数传递,来修改要返回的 promise 的状态。 - 在 catch 中捕获错误,一旦捕获到错误,马上调用
reject
将要返回的 promise 修改为失败(之所以这样做是因为 onFullfilledFn, onRejectedFn 执行中发生了错误,则会立即返回一个错误的 promise)
状态为
REJECT
时- 将接下来的操作注册为微任务
- 将接下来的操作放到 try-catch 中
- 在 try 中调用
onRejectedFn
并将调用then
方法的 promise 的reason
作为参数传递,将返回值赋值给result
, 调用resolve
并将result
作为参数传递,来修改要返回的 promise 的状态。 - 在 catch 中捕获错误,一旦捕获到错误,马上调用
reject
将要返回的 promise 修改为失败(之所以这样做是因为 onFullfilledFn, onRejectedFn 执行中发生了错误,则会立即返回一个错误的 promise)
状态为
PENDING
时
将FULLFILL
,REJECT
时要进行的操作,分别封装到两个箭头函数里面(必须用箭头函数,这样this
才能指向调用then
方法的 promise),在分别添加到调用then
方法的 promise 的onFullfilledFns
andonRejectedFns
中。
# 手写 Promise.all 方法
# 完整代码
function PromiseAll(promises) { | |
return new Promise((resolve, reject) => { | |
// 没有传入参数 or 传入的参数内部没有迭代器的时候会返回一个错误的 promise | |
if (promises == null || typeof promises[Symbol.iterator] !== 'function') { | |
throw new Error('argument is not iterator'); | |
} | |
// 将迭代器对象转化为数组 | |
promises = [...promises]; | |
// 处理空数组的情况 | |
if (promises.length === 0) resolve([]); | |
let resolvedCount = 0; | |
let promiseNum = promises.length; | |
const values = []; | |
promises.forEach((promise, index) => { | |
// 判断数组元素是否为 promise | |
if (!promise instanceof Promise) { | |
promise = Promise.resolve(promise); | |
} | |
promise | |
.then((res) => { | |
resolvedCount++; | |
values[index] = res; | |
if (resolvedCount === promiseNum) resolve(values); | |
}) | |
.catch((err) => { | |
reject(err); | |
}); | |
}); | |
}); | |
} |
# Promise.all 方法实现讲解
- 返回一个 promise, 在 executor 里进行接下来的操作
- 判断传递的参数
promises
是否可迭代,参数是否为空if (promises == null || typeof promises[Symbol.iterator] !== 'function')
进行判断- 如果参数为空 or 参数不能迭代,则调用抛出一个错误,让返回的 promise 状态变为失败
- 如果有参数并且可以迭代,则进行接下来的操作
- 将可迭代的参数转换为数组
promises = [...promises]
- 判断转换为数组后的参数是否为空,并进行相关操作
- 如果数组为空,则调用
resolve
将一个空数组作为参数传递,让返回的 promise 状态变为成功 - 如果数组不为空,则进行接下来的接下来的操作
- 如果数组为空,则调用
- 初始化相关变量
promisesNum = promises.length
保存参数数组元素的数量resolvedCount = 0
保存已经处理过的 promise 的数量values = []
保存处理过的 promise 的value
- 遍历参数数组,并进行相关处理
- 判断数组元素是否为 promise
- 不是 promise 则调用
Promise.resolve()
将其转换为 promise - 是 promise,则直接进行接下来的操作
- 不是 promise 则调用
- 对已经是 promise 的数组元素调用
then
方法- 调用
then
方法,传递 promise 成功时的callback
,在callback
里面将参数res
存储进values
,并且resolvedCount++
计数器加一,resolvedCount === promiseNum
判断已经处理过的数组元素的数量和数组的元素的数量,是否相等,相等时则直接调用resolve(values)
,将返回的 promise 的状态改为成功
- 调用
- 对已经时 promise 的数组元素调用
catch
方法捕获错误- 一旦捕获到错误,直接调用
reject(err)
,将返回的 promise 的状态改为失败
- 一旦捕获到错误,直接调用
- 判断数组元素是否为 promise
# 手写 Promise.any 方法
# 完整代码
function PromiseAny(promises) { | |
return new Promise((resolve, reject) => { | |
if (promises == null || typeof promises[Symbol.iterator] !== 'function') { | |
throw new Error('arrgument is not iterator'); | |
} | |
promises = [...promises]; | |
if (promises.length === 0) resolve([]); | |
let rejectedCount = 0; | |
let promiseNum = promises.length; | |
let errs = []; | |
promises.forEach((promise, index) => { | |
if (!promise instanceof Promise) { | |
promise = Promise.resolve(promise); | |
} | |
promise | |
.then((res) => { | |
resolve(res); | |
}) | |
.catch((err) => { | |
errs[index] = err; | |
rejectedCount++; | |
if (rejectedCount === promiseNum) reject(errs); | |
}); | |
}); | |
}); | |
} |
# 手写 Promise.race 方法
# 完整代码
function promiseRace(promises) { | |
new Promise((resolve, reject) => { | |
if (promises == null || typeof promises[Symbol.iterator] !== 'function') { | |
throw new Error('arrgument is not iterator'); | |
} | |
promises = [...promises]; | |
if (promises.length === 0) resolve([]); | |
promises.forEach((promise) => { | |
if (!promise instanceof Promise) { | |
promise = Promise.resolve(promise); | |
} | |
promise | |
.then((res) => { | |
resolve(res); | |
}) | |
.catch((err) => { | |
reject(err); | |
}); | |
}); | |
}); | |
} |