文章类型:个人学习笔记
手写 Promise 的前提是了解 Promise/A+ 规范,不说倒背如流,但是基本的点还是需要掌握的。
1.Promise/A+规范
原文地址:Promise/A+规范
提醒:翻译是采用个人理解+谷歌翻译逐字逐句翻译
(1)术语:
1.1. “promise” 是一个带有符合此行为规范 then 方法的对象(object)或函数(function)。
1.2. “thenable” 是定义 then 方法的一个对象(object)或函数(function)。
1.3. “value” 可以是任何合法的 JavaScript 值(包括 undefined,thenable,promise)。
1.4. “exception” 是被 throw 语句抛出的值。
1.5. “reason” 是表明 promise 为什么被拒绝的值。
(2)规范
2.1. Promise状态
一个 promise 必须处于以下三种状态之一:pending,fulFilled,或 rejected。
2.1.1. 当 promise 为 pending 状态时:
- 2.1.1.1. 可能会转换为 fulFilled状态 或 rejected状态
2.1.2. 当 promise 为 fulFilled 状态时:
-
2.1.2.1. 不能转换为任何其它状态。
-
2.1.2.2. 必须有一个 value 值,且该 value 值不能更改。
2.1.3. 当 promise 为 rejected 状态时:
-
2.1.3.1. 不能转换为任何其它状态。
-
2.1.3.2. 必须有一个 reason 值,且该 reason 值不能更改。
在此,“不能更改”意味着恒等不变(即:===),但并不表示更深层次的不可改变。
2.2. then方法
一个 Promise 必须提供一个 then 方法用来访问 当前值、最终的 value值 或 reason。
一个 Promise 的 then 方法接收两个参数:
promise.then(onFulfilled, onRejected)
2.2.1. onFulfilled 和 onRejected 都是可选参数:
-
2.2.1.1. 如果 onFulfilled 不是函数,则必须将其忽略。
-
2.2.1.2. 如果 onRejected 不是函数,则必须将其忽略。
2.2.2. 如果 onFulfilled 是一个函数:
-
2.2.2.1. 必须在 promise 实现 fulFilled 状态后调用它,它的第一个参数是 promise 的值。
-
2.2.2.2. 不能在 promise 实现 fulFilled 状态前调用它。
-
2.2.2.3. 它不能被调用超过一次
2.2.3. 如果 onRejected 是一个函数:
-
2.2.3.1. 必须在 promise 实现 rejected 状态后调用它,它的第一个参数是 reason。
-
2.2.3.2. 不能在 promise 实现 rejected 状态前调用它。
-
2.2.3.3. 它不能被调用超过一次
2.2.4. onFulfilled 或 onRejected 不能在执行上下文堆栈仅包含平台代码之前调用。[3.1]
2.2.5. onFulfilled 和 onRejected 必须作为函数调用(即 没有 this 值)。[3.2]
2.2.6. then 可能在同一个 promise 中被多次调用。
-
2.2.6.1. 如果当 promise 是 fulFilled 状态时,所有 then 各自的 onFulfilled 回调必须按照其被 then 发起调用的顺序执行。
-
2.2.6.2. 如果当 promise 是 rejected 状态时,所有 then 各自的 onRejected 回调必须按照其被 then 发起调用的顺序执行。
2.2.7. then 必须返回一个 promise [3.3] 。
promise2 = promise1.then(onFulfilled, onRejected);
- 2.2.7.1. 如果 onFulfilled 或 onRejected 返回一个 value x,运行 promise 解决程序:
[[Resolve]](promise2, x)
-
2.2.7.2. 如果 onFulfilled 或 onRejected 抛出异常 e ,promise2 必须执行 rejected 将 e 作为 reason 的值。
-
2.2.7.3. 如果 onFulfilled 不是一个函数并且 promise1 是 fulFilled 状态,promise2 必须是 fulFilled 状态且带着 promise1 同样的值。
-
2.2.7.4. 如果 onRejected 不是一个函数并且 promise1 是 rejected 状态,promise2 必须是 rejected 状态且带着 promise1 同样的 reason。
2.3. Promise 执行程序 (解决程序)
Promise 解决程序是一个抽象操作表现为输入一个 promise 和一个 value值,我们用 [[Resolve]](promise, x) 表示,如果 x 是一个 thenable,它试图使 promise 采用 x 的状态,假设 x 的行为和 promise 类似。否则,它带着一个 value x 使 promise 转为 fulfilled 状态。
这种对于 thenable 的处理就允许 promise 实现互操作,只要他们暴露一个符合 Promises/A+ 的 then 方法。它也允许Promises/A+ 实现使用合理的 then 方法“整合”不合格的实现。
要运行 [[Resolve]](promise,x),请执行以下步骤:
2.3.1. 如果 promise 和 x 指向同一个对象,promise 带着一个 TypeError 作为 reason 执行 reject。
2.3.2. 如果 x 是一个 promise ,采用它的状态 [3.4] :
-
2.3.2.1。 如果 x 是 pending 状态,promise 必须保持 pending 状态直到 x 是 fulFilled 状态或 rejected 状态。
-
2.3.2.2. 如果当 x 是 fulfilled 状态,带着同样的 value 让 promise 变成 fulFilled 状态。
-
2.3.2.3. 如果当 x 是 rejected 状态,带着同样的 reason 让 promise 变成 rejected 状态。
2.3.3. 除此之外,如果 x 是一个对象或函数:
-
2.3.3.1. 将 x.then 赋值给 then。[3.5]
-
2.3.3.2. 如果使用属性 x.then 引发抛出异常 e,带着 e 作为 reason 让 promise 变成 rejected 状态。
-
2.3.3.3. 如果 then 是一个函数,用 x 作为 this 调用它,第一个参数 resolvePromise,第二个参数 rejectPromise,如下:
-
- 2.3.3.3.1. 如果当 resolvePromise 带着一个 value y 被调用,执行 [[Resolve]](promise, y)。
-
- 2.3.3.3.2. 如果当 rejectPromise 带着一个 reason r 被调用,带着 r 作为 reason 让 promise 变成 rejected 状态。
-
- 2.3.3.3.3. 如果同时调用 resolvePromise 和 rejectPromise ,或者对同一参数进行了多次调用,则第一个调用优先,而所有其他调用均被忽略。
-
- 2.3.3.3.4. 如果调用 then 抛出异常 e:
-
-
- 2.3.3.3.4.1. 如果 resolvePromise 或 rejectPromise已经被调用,请忽略它。
-
-
-
- 2.3.3.3.4.2. 否则,带着 e 作为 reason 让 promise 变成 rejected 状态。
-
2.3.4. 如果 then 不是一个函数,带着 x 作为 value 让 promise 变成 fulFilled 状态。
如果一个 Promise 是 resolved 带着一个 thenable 在一个 thenable 循环链中,这样 [[Resolve]](promise, thenable) 的递归性质最终导致 [[Resolve]](promise, thenable) 再次被调用,遵循上述算法将导致无限递归,鼓励但不是必需的实现,以检测此类递归并以信息 TypeError 为 reason 的执行 promise 的 reject。[3.6]
(3)附注
3.1. 这里的“平台代码”是指引擎,执行环境和 promise 实现代码。实际上,此要求可确保在事件循环回合之后调用 onFulfilled 和 onRejected 异步执行,然后使用新的堆栈进行调用。可以使用 “宏任务” 机制(如 setTimeout 或 setImmediate )或 “微任务” 机制(如 MutationObserver 或 process.nextTick )来实现。由于 promise 实现被视为平台代码,因此它本身可能包含一个任务调度队列或“trampoline”,在其中调用处理程序。
3.2. 也就是说,在严格模式下,this 是 undefined 。 在非严格模式下,this 是全局对象。
3.3. 如果实现满足所有要求,则实现可以允许 promise2 === promise1 。 每个实现都应记录是否可以产生 promise2 === promise1 以及在什么条件下产生。
3.4. 通常,只有 x 来自当前的实现,才知道它是一个真正的 promise 。 本节允许使用特定的实现的方式来采用已知符合 promise 的状态。
3.5. 首先存储对 x.then 的引用,然后测试该引用,然后调用该引用的过程避免了对 x.then 属性的多次访问。 此类预防措施对于确保访问者属性的一致性非常重要,因为访问者属性的值在两次检索之间可能会发生变化。
3.6. 实现不应在可建立链的深度上设置任意限制,并假定超出该任意限制,则递归将是无限的。 只有真正的循环才应该导致 TypeError 。 如果遇到无限多个不同的罐头,则永远递归是正确的行为。
(3)总结
2.对照PromiseA+规范手写 Promise
(1)Promise 状态
1.pending:待定
2.fulFilled:已解决
3.rejected:拒绝
function写的示例
// 1.首先 Promise 有三个状态,初始状态为pending
// 2.当 Promise 为 fulFilled 或 rejected状态时,不能转变成其它状态
// 3.当 Promise 为 fulFilled 或 rejected状态时,
// 必须有一个值(fulFilled是value,rejected是reason)
// 4.Promise 接收一个回调函数
function MyPromise(exector) {
const PENDING = 'pending';
const FULFILLED = 'fulFilled';
const REJECTED = 'rejected';
const that = this;
that.status = PENDING;
function resolve(value) {
// 这里判断的原因就是Promise状态改为FULFILLED,不能再进行改变
if (that.status === PENDING) {
that.status = FULFILLED;
that.value = value;
}
}
function reject(reason) {
// 这里判断的原因跟上面一样
if (that.status === PENDING) {
that.status = REJECTED;
that.value = reason;
}
}
try {
exector(resolve, reject);
} catch (error) {
reject(error);
}
}
上述resolve和reject方法中如果不进行判断,下面这种情况就会再次改变promise的状态:
let p = new MyPromise((resolve, rejrect) => {
reject('拒绝');
resolve('解决');
})
console.log(p);
上面这个例子,如果不进行判断,最终状态会是 fulfilled ,进行判断后状态是 rejected ,不会再被更改,这样才是满足Promise/A+规范的。
class写的示例
class MyPromise {
static PENDING = 'pending';
static FULFILLED = 'fulFilled';
static REJECTED = 'rejected';
constructor(executor) {
this.status = MyPromise.PENDING;
this.value = null;
try {
// 这个地方this.resolve只是拿到resolve方法,并不是this调用的
// 事实上是全局对象调用的,但是class里面默认是严格模式
// 如果不改变resolve的内部this指向,他的this是undefined
executor(this.resolve.bind(this), this.reject.bind(this));
} catch (error) {
this.reject(error)
}
}
resolve(value) {
if (this.status === MyPromise.PENDING) {
this.status = MyPromise.FULFILLED;
this.value = value;
}
}
reject(reason) {
if (this.status === MyPromise.PENDING) {
this.status = MyPromise.REJECTED;
this.reason = reason;
}
}
}
// 测试
let p = new MyPromise(function (resolve, reject) {
reject('XKHM')
});
console.log(p);
2.then方法
promise.then(onFulfilled, onRejected)
1.then方法的两个参数onFulfilled和onRejected都是可选参数,如果这两个不是函数,将被忽略。
2.onFulfilled一定是在Promise执行完状态为fulFilled时调用,并接受一个参数value,并且最多被调用一次
3.onRejected一定是在Promise执行完状态为rejected时调用,并接受一个参数reason,并且最多被调用一次
4.onFulfilled和onRejected需要在下一轮事件循环中调用(异步调用)
5.onFulfilled和onRejected需要作为函数调用(没有自己的this值)
6.then方法可以被Promise链式调用
7.then方法必须返回一个Promise对象
提醒:
(1)then的时候需要处理Promise的三种状态,包括pending状态
(2)如果then本身在没经过处理的情况下就返回的是Promise,需要单独处理
3.Promise解决过程
Promise解决程序是一个抽象的操作,其需输入一个 promise 和一个值,我们表示为 [[Resolve]](promise, x)
1.如果Promise和x指向同一对象,以 TypeError 为据因拒绝执行 promise
2.如果 x 为 promise ,接受其状态
3.抑或 x 为对象或者函数
示例
class MyPromise {
static PENDING = 'pending';
static FULFILLED = 'fulFilled';
static REJECTED = 'rejected';
constructor(executor) {
this.status = MyPromise.PENDING;
this.value = null;
this.callback = [];
try {
// 这个地方this.resolve只是拿到resolve方法,并不是this调用的
// 事实上是全局对象调用的,但是class里面默认是严格模式
// 如果不改变resolve的内部this指向,他的this是undefined
executor(this.resolve.bind(this), this.reject.bind(this));
} catch (error) {
this.reject(error)
}
}
resolve(value) {
if (this.status === MyPromise.PENDING) {
this.status = MyPromise.FULFILLED;
this.value = value;
setTimeout(_ => {
this.callback.forEach(callFn => {
callFn.onFulfilled(value)
});
})
}
}
reject(value) {
if (this.status === MyPromise.PENDING) {
this.status = MyPromise.REJECTED;
this.value = value;
setTimeout(_ => {
this.callback.forEach(callFn => {
callFn.onRejected(value)
});
})
}
}
then(onFulfilled, onRejected) {
if (typeof onFulfilled !== 'function') {
onFulfilled = value => value;
}
if (typeof onRejected !== 'function') {
onRejected = value => value;
}
let promise = new MyPromise((resolve, reject) => {
if (this.status === MyPromise.PENDING) {
this.callback.push({
onFulfilled: value => {
this.parse(promise, onFulfilled(this.value), resolve, reject)
},
onRejected: value => {
this.parse(promise, onRejected(this.value), resolve, reject)
}
})
}
if (this.status === MyPromise.FULFILLED) {
setTimeout(() => {
this.parse(promise, onFulfilled(this.value), resolve, reject)
})
}
if (this.status === MyPromise.REJECTED) {
setTimeout(() => {
this.parse(promise, onRejected(this.value), resolve, reject)
})
}
});
return promise;
}
parse(promise, res, resolve, reject) {
if (promise == res) {
throw new TypeError('then返回的promise不能是跟then是相同的Promise');
}
try {
if (res instanceof MyPromise) {
res.then(resolve, reject)
} else {
resolve(res)
}
} catch (error) {
reject(error)
}
}
}
最终完整示例
class MyPromise {
static PENDING = 'pending';
static FULFILLED = 'fulFilled';
static REJECTED = 'rejected';
constructor(executor) {
this.status = MyPromise.PENDING;
this.value = null;
this.callback = [];
try {
// 这个地方this.resolve只是拿到resolve方法,并不是this调用的
// 事实上是全局对象调用的,但是class里面默认是严格模式
// 如果不改变resolve的内部this指向,他的this是undefined
executor(this.resolve.bind(this), this.reject.bind(this));
} catch (error) {
this.reject(error)
}
}
resolve(value) {
if (this.status === MyPromise.PENDING) {
this.status = MyPromise.FULFILLED;
this.value = value;
setTimeout(_ => {
this.callback.forEach(callFn => {
callFn.onFulfilled(value)
});
})
}
}
reject(value) {
if (this.status === MyPromise.PENDING) {
this.status = MyPromise.REJECTED;
this.value = value;
setTimeout(_ => {
this.callback.forEach(callFn => {
callFn.onRejected(value)
});
})
}
}
then(onFulfilled, onRejected) {
if (typeof onFulfilled !== 'function') {
onFulfilled = value => value;
}
if (typeof onRejected !== 'function') {
onRejected = value => value;
}
let promise = new MyPromise((resolve, reject) => {
if (this.status === MyPromise.PENDING) {
this.callback.push({
onFulfilled: value => {
this.parse(promise, onFulfilled(this.value), resolve, reject)
},
onRejected: value => {
this.parse(promise, onRejected(this.value), resolve, reject)
}
})
}
if (this.status === MyPromise.FULFILLED) {
setTimeout(() => {
this.parse(promise, onFulfilled(this.value), resolve, reject)
})
}
if (this.status === MyPromise.REJECTED) {
setTimeout(() => {
this.parse(promise, onRejected(this.value), resolve, reject)
})
}
});
return promise;
}
parse(promise, res, resolve, reject) {
if (promise == res) {
throw new TypeError('then返回的promise不能是跟then相同的Promise');
}
try {
if (res instanceof MyPromise) {
res.then(resolve, reject)
} else {
resolve(res)
}
} catch (error) {
reject(error)
}
}
static resolve(value) {
return new MyPromise((resolve, reject) => {
if (value instanceof MyPromise) {
value.then(resolve, reject);
} else {
resolve(value);
}
})
}
static reject(reason) {
return new MyPromise((_, reject) => {
reject(reason);
})
}
static all(promises) {
let resolves = [];
return new MyPromise((resolve, reject) => {
promises.forEach((promise, index) => {
promise.then(value => {
resolves.push(value)
if (resolves.length === promises.length) {
resolve(resolves);
}
}, reason => {
reject(reason);
})
})
})
}
static race(promises) {
return new MyPromise((resolve, reject) => {
promises.forEach(promise => {
promise.then(value => {
resolve(value)
})
})
})
}
}
4.测试
npm install promises-aplus-tests -g
npx promises-aplus-tests promise.js
5.提醒
(1)Promise内部错误被吞掉了,那是被谁吞掉的呢,是被reject吞掉的
说明例子
const pending = 'pending';
const fulFilled = 'fulFilled';
const rejected = 'rejected';
const resolveFn = [];
const rejectFn = [];
// Promise/A+规范
// 1. Promise状态
// (1) 当Promise为pending状态时,可以被转换成另外两种状态
// (2) 当Promise为fulFilled状态时,不能被更改,且必须有一个不能被更改的value值
// (3) 当Promise为rejected状态时,不能被更改,且必须有一个不能被更改的reason值
function MyPromise(execute){
const that = this;
that.status = pending;
function resolve(value){
if(that.status === pending){
that.status = fulFilled;
that.value = value;
}
}
function reject(reason){
if(that.status === pending){
that.status = rejected;
that.value = reason;
}
}
// 如果执行中有错误,会立马执行reject
// 也就是说Promise中的错误是被reject吞掉的
// try...catch是捕获不到的
try {
execute(resolve,reject);
} catch (error) {
reject(error)
}
}
// 2.then方法
// 首先,then方法是挂在Promise原型上的一个方法
// (1)then方法接收两个回调函数,如果不是函数忽略
// (2)then方法返回一个Promise
// (3)then方法需要异步执行
// (4)then可以被链式调用
// (5)如果onResolve是函数,必须在Promise状态为fulFilled后调用
// (6)如果onRejected是函数,必须在Promise状态为rejected后调用
// (7)then没有自己的this(也可以理解为then方法是挂在Promise原型上的)
MyPromise.prototype.then = function(onResolve,onRejected){
if(typeof onResolve !== 'function'){
return onResolve = value => value
}
if(typeof onRejected !== 'function'){
return onRejected = reason => reason
}
let that = this;
let p = new MyPromise((resolve,reject) => {
if(that.status === fulFilled){
setTimeout(_ => {
onResolve(that.value)
})
}
if(that.status === rejected){
setTimeout(_ => {
onRejected(that.value)
})
}
})
return p;
}
// 测试
try {
let p = new MyPromise((resolve,reject) => {
throw new TypeError('抛出错误')
})
console.log(p);
} catch (error) {
console.log(error);
}
// p.then();
try {
let p1 = new Promise(resolve => {
throw new TypeError('抛出错误')
})
console.log(p1);
} catch (error) {
console.log(error);
}
Promise实例调用then时,如果then回调函数里面抛出了错误,也会被reject吞掉
6.手写Promise的意义
相当于照着一个规范然后用代码区实现,相当于深入了解源码,只是Promise源码才100多行,算阅读一个小型项目的源码吧。
手写Promise完整代码
const pending = 'pending';
const fulFilled = 'fulFilled';
const rejected = 'rejected';
// Promise/A+规范
// 1. Promise状态
// (1) 当Promise为pending状态时,可以被转换成另外两种状态
// (2) 当Promise为fulFilled状态时,不能被更改,且必须有一个不能被更改的value值
// (3) 当Promise为rejected状态时,不能被更改,且必须有一个不能被更改的reason值
function MyPromise(execute){
const that = this;
that.status = pending;
that.resolveFn = [];
that.rejectFn = [];
function resolve(value){
setTimeout(_ => {
if(that.status === pending){
that.status = fulFilled;
that.value = value;
that.resolveFn.forEach(f => {
f(that.value)
})
}
})
}
function reject(reason){
setTimeout(_ => {
if(that.status === pending){
that.status = rejected;
that.value = reason;
that.rejectFn.forEach(f => {
f(that.value)
})
}
})
}
// 如果执行中有错误,会立马执行reject
// 也就是说Promise中的错误是被reject吞掉的
// try...catch是捕获不到的
try {
execute(resolve,reject);
} catch (error) {
reject(error)
}
}
// 2.then方法
// 首先,then方法是挂在Promise原型上的一个方法
// (1)then方法接收两个回调函数,如果不是函数忽略
// (2)then方法返回一个Promise
// (3)then方法需要异步执行
// (4)then可以被链式调用
// (5)如果onResolve是函数,必须在Promise状态为fulFilled后调用
// (6)如果onRejected是函数,必须在Promise状态为rejected后调用
// (7)then没有自己的this(也可以理解为then方法是挂在Promise原型上的)
MyPromise.prototype.then = function(onResolve,onRejected){
if(typeof onResolve !== 'function'){
onResolve = value => value
}
if(typeof onRejected !== 'function'){
onRejected = reason => {
throw reason
}
}
const that = this;
let promise;
if(that.status === fulFilled){
promise = new MyPromise((resolve,reject) => {
setTimeout(_ => {
try {
let x = onResolve(that.value);
resolvePromise(promise,x,resolve,reject)
} catch (error) {
reject(error)
}
})
})
}
if(that.status === rejected){
promise = new MyPromise((resolve,reject) => {
setTimeout(_ => {
try {
let x = onRejected(that.value);
resolvePromise(promise,x,resolve,reject)
} catch (error) {
reject(error)
}
})
})
}
if(that.status === pending){
promise = new MyPromise((resolve,reject) => {
that.resolveFn.push(_ => {
try {
let x = onResolve(that.value);
resolvePromise(promise,x,resolve,reject)
} catch (error) {
reject(error)
}
})
that.rejectFn.push(_ => {
try {
let x = onRejected(that.value);
resolvePromise(promise,x,resolve,reject)
} catch (error) {
reject(error)
}
})
})
}
return promise;
}
// 3.Promise执行程序
// 1.promise和x指向同一个对象,执行reject()
// 2.x是promise,采用它的状态
// 3.x是对象或函数,处理
// 4.then不是函数
function resolvePromise(promise,x,resolve,reject){
if(promise === x){
return reject(new TypeError('promise不能和x相等'))
}
if(x instanceof MyPromise){
if(x.status === fulFilled){
resolve(x.value)
}else if(x.status === rejected){
reject(x.value)
}else{
x.then(y => {
resolvePromise(promise,y,resolve,reject)
},reject)
}
}else if(x !== null && (typeof x === 'object' || typeof x === 'function')){
let executed;
try {
let then = x.then;
if(typeof then === 'function'){
then.call(x,y => {
if(executed){
return false
}
executed = true;
resolvePromise(promise,y,resolve,reject)
},e => {
if(executed){
return false
}
executed = true;
reject(e);
})
}else{
resolve(x)
}
} catch (error) {
if(executed){
return false
}
executed = true;
reject(error);
}
}else{
resolve(x)
}
}