手写Promise
步骤
- 编写 构造函数
- 定义类、构造器、内部
resolve
、reject
- 传递
resolve
,reject
给构造器参数
- 定义类、构造器、内部
- 定义 状态和原因
- 用常量标记
state
的值 - 定义
state
和result
- 在
resolve
和rejected
中修改state
和result
- 用常量标记
- 实现 then方法
- 定义
then
方法,参数列表为onFulFilled
,onRejected
- 判断参数类型,进行修改
- 根据
state
判定执行哪个回调- 如果是
pending
,将onFulFilled
,onRejected
存入#handle
(私有属性,用来存放调用resolve/rejected
之前,then
方法的参数列表),然后在构造函数的resolve
和reject
中遍历#handle
提取对应的回调 - 如果是
fulfilled
,执行onFulfilled
- 如果是
rejected
, 执行onRejected
- 如果是
- 定义
- 异步任务
- 选择可以用来实现异步的方法
queueMicrotask
、MutationObserver
、setTimeout
- 封装实现异步的方法
- 传递回调函数,需要使用指数函数进行包裹,不然会报错
- 选择可以用来实现异步的方法
- 链式编程
then
方法返回一个MyPromise
对象- 返回值
- 返回正常值,将值给
resolve
或者reject
- 抛出错误,调用
reject
- 返回了返回值本身(重复引用),判断是否相等,相等则抛出错误
- 返回正常值,将值给
- 抽取步骤三作为一个函数,重复引用
- 实例方法
catch(onRejected)
: 捕获错误,如果 then 没有捕获到的话,此方法是 Promise.prototype.then(undefined, onRejected) 的一种简写形式- 实现: 直接返回
this.then(undefined, onRejected)
- 实现: 直接返回
finally()
: Promise 实例的 finally() 方法用于注册一个在 promise 敲定(兑现或拒绝)时 调用的函数。它会立即返回一个等效的 Promise 对象,这可以允许你链式调用其他 promise 方法- 实现: 直接返回
this.then(onFinally, onFinally)
- 实现: 直接返回
- 静态方法(实现参考文档描述)
resolve(value)
:- 功能:Promise.resolve() 静态方法将给定的值转换为一个 Promise 。如果该值本身就是一个 Promise,那么该 Promise 将被返回;如果该值是一个 thenable 对象,Promise.resolve() 将调用其 then() 方法及其两个回调函数;否则,返回的 Promise 将会以该值兑现
- 实现:(判断参数类型)
- 如果传入的 value 的类型是 MyPromise 则直接返回value,
- 否则实例化一个MyPromise对象,调用 resolve ,将 value 作为参数传入
reject(reason)
:- 功能:Promise.reject() 静态方法返回一个已拒绝(rejected)的 Promise 对象,拒绝原因为给定的参数
- 实现:(不用判断参数类型)
- 实例化一个 MyPromise ,调用 reject ,将 reason 作为参数传入
race(iterable)
: 返回首个成功的,不管是 resolve 还是 reject- 功能:Promise.race() 静态方法接受一个 promise 可迭代对象作为输入,并返回一个 Promise。这个返回的 promise 会随着第一个 promise 的敲定而敲定
- 实现:(判断类型)
- 实例化一个 MyPromise
- 如果 iterable 不是可迭代对象(判断是否是数组),则调用
reject(new TypeError('Argument is not iterable'))
- 否则遍历 iterable (使用forEach遍历),每一个对象调用静态方法
MyPromise.resolve().then()
(因为无法判断每一个元素的类型,但是我们需要,如果是MyPromise的话返回 resolve/reject 的结果,如果是非 MyPromise 则返回自身值)
all(iterable)
: 如果迭代对象有一个元素被拒绝则返回的 MyPromise 拒绝,否则返回所有成功的 resolve(results),空迭代对象,则返回 resolve(iterable)- 功能:Promise.all() 静态方法接受一个 Promise 可迭代对象作为输入,并返回一个Promise。当所有输入的 Promise 都被兑现时,返回的 Promise 也将被兑现(即使传入的是一个空的可迭代对象),并返回一个包含所有兑现值的数组。如果输入的任意 Promise 被拒绝,则返回的 Promise 将被拒绝,并带有第一个被拒绝的原因
- 实现:(判断类型)
- 实例化一个 MyPromise
- iterable 不是可迭代对象(判断是否是数组),则调用
reject(new TypeError('Argument is not iterable'))
- iterable为空,则调用
resolve(iterable)
- iterable可迭代且不为空,遍历 iterable (使用forEach遍历),每一个对象调用静态方法
MyPromise.resolve().then()
,定义返回 数组(results),记录兑现次数(count) - 如果兑现,将结果存入 results ,count 加一
- 如果 count 等于迭代对象的长度 ,则兑现(调用resolve(results)),如果拒绝则拒绝(调用 reject)
allSettled
: 无论迭代对象的元素是兑现还是拒绝都将 结果和状态 存入result,最后兑现,参数是results- 功能:Promise.allSettled() 静态方法将一个 Promise 可迭代对象作为输入,并返回一个单独的 Promise。当所有输入的 Promise 都已敲定时(包括传入空的可迭代对象时),返回的 Promise 将被兑现,并带有描述每个 Promise 结果的对象数组。
- 实现:(判断类型)
- 实例化一个 MyPromise
- iterable 不是可迭代对象(判断是否是数组),则调用
reject(new TypeError('Argument is not iterable'))
- iterable为空,则调用
resolve(iterable)
- iterable可迭代且不为空,遍历 iterable (使用forEach遍历),每一个对象调用静态方法
MyPromise.resolve().then()
,定义返回 数组(results),记录兑现次数(count) - 如果兑现,将
{ status: FULFILLED, value: res }
存入 results ,如果拒绝,将{ status: REJECTED, reason: err }
,count 加一 - 如果 count 等于迭代对象的长度 ,则兑现(调用resolve(results))
any
: 如果迭代对象有一个元素被兑现则返回的 MyPromise 兑现,否则返回一个 AggregateError,参数是所有拒绝的原因,空迭代对象,则返回 reject(new AggregateError(promises, 'All promises were rejected'))- 功能:Promise.any() 静态方法将一个 Promise 可迭代对象作为输入,并返回一个 Promise。当输入的任何一个 Promise 兑现时,这个返回的 Promise 将会兑现,并返回第一个兑现的值。当所有输入 Promise 都被拒绝(包括传递了空的可迭代对象)时,它会以一个包含拒绝原因数组的 AggregateError 拒绝
- 实现:
- 实例化一个 MyPromise
- iterable 不是可迭代对象(判断是否是数组),则调用
reject(new TypeError('Argument is not iterable'))
- iterable为空,则调用
reject(new AggregateError(promises, 'All promises were rejected'))
- iterable可迭代且不为空,遍历 iterable (使用forEach遍历),每一个对象调用静态方法
MyPromise.resolve().then()
,定义返回数组(errors),记录兑现次数(count) - 如果兑现,则兑现(调用resolve(res))
- 如果拒绝,则将原因存入 errors
- 如果所有都拒绝并且count 等于迭代对象的长度 ,则拒绝(调用
reject(new AggregateError(errors, 'All promises were rejected'
)))
异步依赖api
js
function runAsynctask(callback) {
// console.log(typeof callback);
if (typeof queueMicrotask === 'function') {
queueMicrotask(callback)
} else if (typeof MutationObserver === 'function') {
const obs = new MutationObserver(callback)
const divNode = document.createElement('div')
obs.observe(divNode, { childList: true })
divNode.innerText = 'divNode'
} else {
setTimeout(callback, 0)
}
}
链式编程依赖api
js
function resolvePromise(p2, x, resolve, reject) {
// 2.3.3.1 如果p2和x引用同一个对象,通过TypeError作为原因来拒绝pormise
if (x === p2) {
throw new TypeError('Chaining cycle detected for promise');
}
/**
* 2.3.3.2 如果x是一个promise,采用他的状态
* 2.3.3.3.1 如果x是pengding状态,promise必须保持等待状态,直到x被fulfilled或rejected
* 2.3.3.3.2 如果x是fulfilled状态,用相同的原因解决promise
* 2.3.3.3.3 如果x是rejected状态,用相同的原因拒绝promise
* */
if (x instanceof MyPromise) {
x.then(y => {
resolvePromise(p2, y, resolve, reject)
}, reject);
}
// 2.3.3 如果x是一个对象或者函数
else if (x !== null && ((typeof x === 'object' || (typeof x === 'function')))) {
// 2.3.3.1 让then成为x.then
try {
var then = x.then;
} catch (e) {
// 2.3.3.2 如果检索属性x.then抛出了异常e,用e作为原因拒绝promise
return reject(e);
}
/**
* 2.3.3.3 如果then是一个函数,通过call调用他,并且将x作为他的this(参数1)
* 调用then时传入2个回调函数:
* 第一个参数叫做resolvePromise(对应到的参数2)
* 第二个参数叫做rejectPromise(对应到参数3)
* */
if (typeof then === 'function') {
// 2.3.3.3.3 如果 resolvePromise 和 rejectPromise 均被调用,或者同一参数被调用了多次,只采用第一次调用,后续的调用会被忽略(观察called后续的赋值+判断)
let called = false;
try {
then.call(
x,
// 2.3.3.3.1 如果 resolvePromise 以 成功原因 y 为参数被调用,继续执行 resolvePromise
y => {
if (called) return;
called = true;
resolvePromise(p2, y, resolve, reject);
},
// 2.3.3.3.2 如果 rejectPromise 以拒绝原因 r 为参数被调用,用 r 拒绝 promise
r => {
if (called) return;
called = true;
reject(r);
}
)
}
// 2.3.3.3.4 如果调用then抛出异常
catch (e) {
// 2.3.3.3.4.1 如果resolvePromise或rejectPromise已经被调用,忽略它
if (called) return;
called = true;
// 2.3.3.3.4.2 否则以 e 作为拒绝原因 拒绝promise
reject(e);
}
} else {
// 2.3.3.4 如果then不是函数,用 x 作为原因 兑现promise
resolve(x);
}
} else {
// 2.3.4 如果then不是对象或函数,用 x 作为原因 兑现promise
return resolve(x);
}
}
Promise/A+ 规范
-
使用 CommonJS 的模块化语法暴露出去
-
提供
deferred
方法,返回对象{promise,resolve,reject}
jsmodule.exports = { deferred() { const res = {} res.promise = new MyPromise((resolve, reject) => { res.resolve = resolve res.reject = reject }) return res } }
-
promise: pending 状态的 promise 实例
-
resolve: 以传入的原因兑现 promise
-
reject: 以传入的原因拒绝 promise
-
-
下包
- 初始化项目
npm init -y
npm i promises-aplus-tests -D
- 初始化项目
-
在
package.json
中配置json"scripts": { "test": "promises-aplus-tests MyPromise" },
-
运行
npm run test
代码汇总
MyPromise.js
js
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise {
state = PENDING;
result = undefined;
#handlers = [];
constructor(func) {
const resolve = (result) => {
if (this.state === PENDING) {
this.state = FULFILLED;
this.result = result;
this.#handlers.forEach(({ onFulfilled }) => {
onFulfilled(this.result);
});
}
};
const reject = (result) => {
if (this.state === PENDING) {
this.state = REJECTED;
this.result = result;
this.#handlers.forEach(({ onRejected }) => {
onRejected(this.result);
});
}
};
try {
func(resolve, reject);
} catch (error) {
reject(error)
}
}
// 实例方法
then(onFulfilled, onRejected) {
// 判断类型
onFulfilled = typeof onFulfilled === "function" ? onFulfilled : x => x
onRejected = typeof onRejected === "function" ? onRejected : x => { throw x }
const p2 = new MyPromise((resolve, reject) => {
if (this.state === FULFILLED) {
// 参数需要使用指数函数,不然queueMicrotask会报错
runAsynctask(() => {
try {
const x = onFulfilled(this.result);
resolvePromise(p2, x, resolve, reject);
} catch (error) {
reject(error);
}
});
} else if (this.state === REJECTED) {
runAsynctask(() => {
try {
const x = onRejected(this.result);
resolvePromise(p2, x, resolve, reject);
} catch (error) {
reject(error);
}
});
} else if (this.state === PENDING) {
this.#handlers.push({
onFulfilled: () => {
runAsynctask(() => {
try {
const x = onFulfilled(this.result);
resolvePromise(p2, x, resolve, reject);
} catch (error) {
reject(error);
}
})
},
onRejected: () => {
runAsynctask(() => {
try {
const x = onRejected(this.result);
resolvePromise(p2, x, resolve, reject);
} catch (error) {
reject(error);
}
});
},
});
}
});
return p2;
}
catch(onRejected) {
return this.then(undefined, onRejected)
}
finally(onFinally) {
return this.then(onFinally, onFinally)
}
// 静态方法
static resolve(value) {
if (value instanceof MyPromise) {
return value
}
return new MyPromise(resolve => {
resolve(value)
})
}
static reject(reason) {
return new MyPromise((undefined, reject) => {
reject(reason)
})
}
static race(iterable) {
return new MyPromise((resolve, reject) => {
if (!Array.isArray(iterable)) {
reject(new TypeError("Argument is not iterable"))
}
iterable.forEach(item => {
MyPromise.resolve(item).then(res => { resolve(res) }, err => { reject(err) })
})
})
}
// 手写promise.all()
// 1. 定义,返回MyPromise,判断参数是否是数组
// 2. 如果数组是空,返回数组
// 3. 遍历数组,处理全部参数,
// 4. 定义返回的数组和标记(是否所有参数都执行完毕)
// 5. 返回的顺序需要和传入的一致,所以利用索引进行存入
// 6. 如果有抛出错误,则直接执行reject,跳出函数
static all(iterable) {
return new MyPromise((resolve, reject) => {
if (!Array.isArray(iterable)) {
reject(new TypeError("Argument is not iterable"))
}
iterable.length === 0 && resolve(iterable)
const results = []
let count = 0
iterable.forEach((item, index) => {
MyPromise.resolve(item).then(res => {
results[index] = res
count++
count === iterable.length && resolve(results)
}, err => {
reject(err)
})
})
})
}
static allSettled(iterable) {
return new MyPromise((resolve, reject) => {
if (!Array.isArray(iterable)) {
reject(new TypeError("Argument is not iterable"))
}
iterable.length === 0 && resolve(iterable)
const results = []
let count = 0
iterable.forEach((item, index) => {
MyPromise.resolve(item).then(res => {
results[index] = { status: FULFILLED, value: res }
count++
count === iterable.length && resolve(results)
}, err => {
results[index] = { status: REJECTED, reason: err }
count++
count === iterable.length && resolve(results)
})
})
resolve(arr)
})
}
static any(iterable) {
return new MyPromise((resolve, reject) => {
// 如果不是数组
if (!Array.isArray(iterable)) {
reject(new TypeError("undefined is not iterable (cannot read property Symbol(Symbol.iterator))"))
}
// 如果数组为空
iterable.length === 0 && reject(new AggregateError(iterable, 'All promises were rejected'))
// 如果有一个可兑现,直接resolve
// 如果都被拒绝,reject
const results = []
let count = 0
iterable.forEach((item, index) => {
MyPromise.resolve(item).then(res => {
resolve(res)
}, err => {
results[index] = { status: REJECTED, reason: err }
count++
count === iterable.length && reject(new AggregateError(results, 'All promises were rejected'))
})
})
resolve(arr)
})
}
}
function runAsynctask(callback) {
if (typeof queueMicrotask === "function") {
queueMicrotask(callback);
} else if (typeof MutationObserver === "function") {
const obs = new MutationObserver(callback);
const divNode = document.createElement("div");
obs.observe(divNode, { childList: true });
divNode.innerText = "divNode";
} else {
setTimeout(callback, 0);
}
}
function resolvePromise(p2, x, resolve, reject) {
// 2.3.3.1 如果p2和x引用同一个对象,通过TypeError作为原因来拒绝pormise
if (x === p2) {
throw new TypeError('Chaining cycle detected for promise');
}
/**
* 2.3.3.2 如果x是一个promise,采用他的状态
* 2.3.3.3.1 如果x是pengding状态,promise必须保持等待状态,直到x被fulfilled或rejected
* 2.3.3.3.2 如果x是fulfilled状态,用相同的原因解决promise
* 2.3.3.3.3 如果x是rejected状态,用相同的原因拒绝promise
* */
if (x instanceof MyPromise) {
x.then(y => {
resolvePromise(p2, y, resolve, reject)
}, reject);
}
// 2.3.3 如果x是一个对象或者函数
else if (x !== null && ((typeof x === 'object' || (typeof x === 'function')))) {
// 2.3.3.1 让then成为x.then
try {
var then = x.then;
} catch (e) {
// 2.3.3.2 如果检索属性x.then抛出了异常e,用e作为原因拒绝promise
return reject(e);
}
/**
* 2.3.3.3 如果then是一个函数,通过call调用他,并且将x作为他的this(参数1)
* 调用then时传入2个回调函数:
* 第一个参数叫做resolvePromise(对应到的参数2)
* 第二个参数叫做rejectPromise(对应到参数3)
* */
if (typeof then === 'function') {
// 2.3.3.3.3 如果 resolvePromise 和 rejectPromise 均被调用,或者同一参数被调用了多次,只采用第一次调用,后续的调用会被忽略(观察called后续的赋值+判断)
let called = false;
try {
then.call(
x,
// 2.3.3.3.1 如果 resolvePromise 以 成功原因 y 为参数被调用,继续执行 resolvePromise
y => {
if (called) return;
called = true;
resolvePromise(p2, y, resolve, reject);
},
// 2.3.3.3.2 如果 rejectPromise 以拒绝原因 r 为参数被调用,用 r 拒绝 promise
r => {
if (called) return;
called = true;
reject(r);
}
)
}
// 2.3.3.3.4 如果调用then抛出异常
catch (e) {
// 2.3.3.3.4.1 如果resolvePromise或rejectPromise已经被调用,忽略它
if (called) return;
called = true;
// 2.3.3.3.4.2 否则以 e 作为拒绝原因 拒绝promise
reject(e);
}
} else {
// 2.3.3.4 如果then不是函数,用 x 作为原因 兑现promise
resolve(x);
}
} else {
// 2.3.4 如果then不是对象或函数,用 x 作为原因 兑现promise
return resolve(x);
}
}
module.exports = {
deferred() {
const res = {}
res.promise = new MyPromise((resolve, reject) => {
res.resolve = resolve
res.reject = reject
})
return res
}
}