Terminology
- “promise” is an object or function with a then method whose behavior conforms to this specification.
- “thenable” is an object or function that defines a then method.
- “value” is any legal JavaScript value (including undefined, a thenable, or a promise).
- “exception” is a value that is thrown using the throw statement.
- “reason” is a value that indicates why a promise was rejected.
promise
是一个拥有 then⽅法的对象或者是函数,其行为遵循本规范thenable
是一个定义了then方法的对象或者函数value
是任意类型的JavaScript值(包括undefined, thenabel或promise)- 是promise状态成功时的值,也就是resolve的参数
exception
是由 throw 抛出的异常值reason
是promise状态失败时的值,也就是reject的参数,表示拒绝的原因
Promise States
A promise must be in one of three states: pending, fulfilled, or rejected.
- When
pending
, a promise:- may transition to either the fulfilled or rejected state.
- When
fulfilled
, a promise:- must not transition to any other state.
- must have a
value
, whichmust not change
.
- When
rejected
, a promise:- must not transition to any other state.
- must have a
reason
, whichmust not change
.
- Here, “must not change” means immutable identity (i.e. ===), but does not imply deep immutability.
有三种状态, 他们之间的关系
pending
- 初始状态,可改变
- 在resolve和reject前都处于这个状态
- 通过resolve -> fulfilled状态
- 通过reject -> rejected状态
fulfilled
- 最终态, 不可变
- 一个promise被resolve之后会变成这个状态
- 必须拥有一个value
rejected
- 最终态,不可变
- 一个promise被reject之后会变成这个状态
- 必须拥有reason
总结:
pending -> resolve(value) -> fulfilled
pending -> reject(reason) -> rejected
then
A promise 必须提供一个then方法, 用来访问当前或最终的value
或reason
.
promise.then(onFulfilled, onRejected)
onFulfilled
和onRejected
都是可选参数- onFulfilled必须是函数类型, 如果传入的不是函数, 应该被忽略
- onRejected必须是函数类型, 如果传入的不是函数, 应该被忽略
如果onFulfilled是一个函数
- 在promise状态由pending变成fulfilled之后, 应该调用onFulfilled, 参数是value.(onFulfilled执行时机?)
- 换句话说,onFulfiled必须在promise is fulfilled之后调用, value值作为它的第一个参数
- 在promise变成fulfilled之前, 不应该调用onFulfilled.
- 只能调用一次 (怎么实现只调用一次?)
- 在promise状态由pending变成fulfilled之后, 应该调用onFulfilled, 参数是value.(onFulfilled执行时机?)
如果onRejected是一个函数
- 在promise变成 rejected 时, 调用onRejected, 参数是reason.
- 在promise变成 rejected 之前, 不应该调用 onRejected.
- 只能被调用一次
onFulfilled 和 onRejected 应该是微任务阶段执行. (此处参考规范)
- 实现promise的时候, 如何去生成微任务?
onFulfilled 和 onRejected 必须作为函数被调用(没有this值)
- must be called as functions (i.e. with no this value)
同一个promise上的then方法可以被调用多次
- promise状态 变成 fulfilled 后, 所有相应的
onFulfilled
回调都必须按照他们原始调用then的顺序执行- 即
promise.then(onFulfilled, onRejected).then(onFulfilled1)
, 按照 .then 的顺序执行
- 即
- promise状态 变成 rejected 后, 所有相应的
onRejected
回调都必须按照他们原始调用then的顺序执行- 所以在实现的时候需要⼀个数组来存放多个
onRejected
的回调
- 所以在实现的时候需要⼀个数组来存放多个
- promise状态 变成 fulfilled 后, 所有相应的
then必须返回一个promise
jsconst promise2 = promise1.then(onFulfilled, onRejected)
- 如果
onFulfilled
或onRejected
执行结果为x(return a value x), 调用[[Resolve]](promise2, x)
[[Resolve]] -> resolvePromise方法
- 如果 onFulfilled 或者 onRejected 执⾏时抛出异常e, promise2必须以e作为reason被rejected
- 如果 onFulfilled 不是一个函数,并且promise1 is fulfilled(已完成), promise2 必须用promise1的相同的value触发fulfilled
- 如果 onRejected 不是一个函数, 并且promise1 is rejected(被拒绝), promise2 必须用promise1的相同的reason 触发rejected
- 如果
The Promise Resolution Procedure
promise处理过程是一个抽象操作,它以一个promise和一个值作为输入,我们表示为[[Resolve]](promise, x)
.
如果x
有then
方法(thenable
),并且看上去像一个Promise,[[Resolve]]
解决程序尝试使promise
接受x
的状态(make promise adopt the state of x); 否则, 用x
的值来执行promise.
(it fulfills promise with the value x.)
这种对thenables
的处理使得Promis的实现更具通用性: 只要暴露一个遵循Promise/A+规范的then方法即可.
[[Resolve]](promise, x)
需要执行以下步骤:
- 如果
promise
和x
指向同一对象(refer to the same object
),以TypeError
作为reason拒绝promise.(reject promise with a TypeError as the reason.
) - 如果
x
是一个promise
,采用它的状态:- 如果
x
处于等待状态,promise
需保持为等待状态,直到x
已完成或被拒绝(is fulfilled or rejected
) - 如果
x
处于执行态, 用相同的值执行promise
(If/when x is fulfilled, fulfill promise with the same value
) - 如果
x
处于拒绝态, 用相同的reason拒绝promise
- 如果
- 否则,如果
x
是一个对象或函数: (不常见)- Let
then
bex.then
[3.5] - 如果取
x.then
的值时(retrieving)抛出异常e
(exception e), 则以e
为reason
拒绝promise
- 如果
then
是函数,让x
做为this
调用它,then
方法传递两个回调函数作为参数,第一个参数叫做resolvePromise
,第二个参数叫做rejectPromise
- 当
resolvePromise
以值y
为参数被调用, 运行[[Resolve]](promise, y)
- 当
rejectPromise
以拒因(reason)r
为参数被调用时,以r
拒绝promise
- 如果
resolvePromise
和rejectPromise
均被调⽤,或者被同⼀参数调⽤了多次,则优先采⽤⾸次调⽤并忽略其他的调⽤ - 如果调⽤
then
⽅法抛出了异常e
- 如果 resolvePromise 或 rejectPromise 已经被调⽤,则忽略
- 否则, 以
e
作为reason拒绝promise (reject promise with
eas the reason.
)
- 当
- 如果
then
不是函数,以x
为参数将 promise 变为已完成状态 (fulfill promise with x
)
- Let
- 如果
x
不是对象或函数, 以x
为参数将promise
变为已完成状态 (fulfill promise with x
). - 重要且常见!
如果promise
用一个循环的thenable链
解决,由于[[Resolve]](promise, thenalbe)
的递归特性,最终将导致[[Resolve]](promise, thenable)
被再次调用,上述算法将会导致无限递归, 鼓励(但不是必须)实现检测这种递归,并以TypeError
作为拒绝promise
的理由.
Notes
3.5 这个过程首先存储对 x.then 的引用,然后测试该引用,然后调用该引用,从而避免对 x.then 属性的多次访问;这种预防措施对于确保访问属性的一致性非常重要,访问属性的值
可能在检索之间(between retrievals 读取)发生变化
Tips
以上, 按着我的理解, 将Promises/A+规范基本整体翻译了一遍, 除了一些Notes注解部分未翻译, 大家可以参考规范自行翻译. 下面, 我们来自己实现一个Promises/A+
.
一步步实现一个Promise
- const promise = new Promise() 代表 Promise是一个构造函数或者class
- 定义三种状态.
- 初始化状态
- resolve 和 reject 方法
- 这两个方法要更改status, 从pending变成fulfilled / rejected
- 入参分别是 value / reason
- 对于实例化promise时的入参处理
- 入参是一个函数, 接收 resolve reject 两个参数
- 初始化promise的时候, 就要同步执行这个函数, 并且有任何的报错都要通过reject抛出去
- then 方法js
const promise2 = promise1.then(onFulfilled, onRejected)
- then 接收两个可选参数, onFulfilled 和 onRejected
- 检查并处理参数, 如果参数不是函数, 就忽略
- 根据当前promise的状态, 调用不同的函数
- 首先我们要拿到所有的回调, 新建两个数组, 分别存储成功和失败的回调, 调用then的时候, 如果还是pending就存入数组.
- 在status发生变化的时候, 执行回调, 用到getter setter, 监听status的变化, 在发生变化的时候来做对应的操作.
- then 的返回值
- 如果 onFulfilled 或者 onRejected 抛出一个异常 e ,那么新的promise2必须reject e
- 返回值必须是一个promise
- 如果 onFulfilled 不是函数, 且 promise1 成功执行, 那么promise2必须返回同样的状态(成功执行)和value.
- 如果 onRejected 不是函数, 且promise1 拒绝执行, promise2必须返回同样的状态(拒绝)和reason.
- 如果 onFulfilled 或者 onRejected 返回一个值 x, 运行resolvePromise方法
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise {
onFulfilledCallbacks = [] // 实例属性
onRejectedCallbacks = []
// _status = PENDING
constructor(executor){
this.status = PENDING
this.value = undefined
this.reason = undefined
// this.onFulfilledCallbacks = [] 写上面和这里是一样的
// this.onRejectedCallbacks = [] // 实例属性
try {
executor(this.resolve.bind(this),this.reject.bind(this))
} catch (e) {
this.reject(e)
}
}
// 利用getter, setter监听status改变 其实可以不用这里监听
// get status() {
// return this._status
// }
// set status(newStatus) {
// this._status = newStatus
// switch (newStatus) {
// case FULFILLED: {
// this.FULFILLED_CALLBACK_LIST.forEach(callback => {
// callback(this.value);
// });
// break;
// }
// case REJECTED: {
// this.REJECTED_CALLBACK_LIST.forEach(callback => {
// callback(this.reason);
// });
// break;
// }
// }
// }
resolve(value) {
if (this.status === PENDING) {
this.value = value;
this.status = FULFILLED;
this.onFulfilledCallbacks.forEach(cb=>cb(this.value));
}
}
reject(reason) {
if (this.status === PENDING) {
this.reason = reason;
this.status = REJECTED;
this.onRejectedCallbacks.forEach(cb=>cb(this.reason));
}
}
then(onFulfilled, onRejected) {
const realOnFulfilled = this.isFunction(onFulfilled) ? onFulfilled : value => value
const realOnRejected = this.isFunction(onRejected) ? onRejected : reason => {throw reason}
// 为了链式调用这里直接创建一个 MyPromise,并在后面 return 出去
const promise2 = new MyPromise((resolve,reject)=>{
// fulfilled处理
const fulfilledMicrotask = () => {
// 创建一个微任务等待 promise2 完成初始化
queueMicrotask(() => {
try {
// 获取成功回调函数的执行结果
const x = realOnFulfilled(this.value);
// 传入 resolvePromise 集中处理
this.resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e)
}
})
}
// rejected处理
const rejectedMicrotask = () => {
queueMicrotask(() => {
try {
// 调用失败回调,并且把原因返回
const x = realOnRejected(this.reason);
this.resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
})
}
// 判断状态
switch (this.status) {
case FULFILLED: {
fulfilledMicrotask();break;
}
case REJECTED: {
rejectedMicrotask();break;
}
case PENDING: {
this.onFulfilledCallbacks.push(fulfilledMicrotask)
this.onRejectedCallbacks.push(rejectedMicrotask)
}
}
})
// then 返回promise2 实现链式调用
return promise2
} // end then
resolvePromise(promise2, x, resolve, reject){
/* 2.3.1 */
if (promise2 === x) {
return reject(new TypeError('Chaining cycle detected for promise #<Promise>'));
}
/* 2.3.2 */
if (x instanceof MyPromise) {
// 如果 x 为 Promise ,则使 promise2 接受 x 的状态
// 也就是继续执行x,如果执行的时候拿到一个y,还要继续解析y
// 这个if跟下面判断then然后拿到执行其实重复了,可有可无
x.then((y) => {
this.resolvePromise(promise2, y, resolve, reject);
}, reject);
}
/* 2.3.3 */
if (x!=null&&(typeof x === 'object' || typeof x === 'function')) {
let then = null;
try {
/* 2.3.3.1 */
then = x.then; // 把 x.then 赋值给 then
} catch (e) {
/* 2.3.3.2 */
return reject(e);
}
/* 2.3.3.3 */
if (typeof then === 'function') {
let called = false; // 标记是否调用过
// 将 x 作为函数的作用域 this 调用
// 传递两个回调函数作为参数,第一个参数叫做 resolvePromise ,第二个参数叫做 rejectPromise
try {
then.call(
x,
// 如果 resolvePromise 以值 y 为参数被调用,则运行 resolvePromise
(y) => {
/* 2.3.3.3.1 */
// 需要有一个变量called来保证只调用一次.
if (called) return;
called = true; /* 2.3.3.3.3 */
this.resolvePromise(promise2, y, resolve, reject);
},
// 如果 rejectPromise 以据因 r 为参数被调用,则以据因 r 拒绝 promise
(r) => {
/* 2.3.3.3.2 */
if (called) return;
called = true; /* 2.3.3.3.3 */
reject(r);
})
/* 2.3.3.3.4 */
} catch (e) {
// 如果调用 then 方法抛出了异常 e:
if (called) return;
// 否则以 e 为据因拒绝 promise
reject(e);
}
}
/* 2.3.3.4 如果 then 不是函数,以 x 为参数执行 promise*/
if (typeof then !== 'function') resolve(x)
}
/* 2.3.4 // 如果 x 不为对象或者函数,以 x 为参数执行 promise*/
if (typeof x !== 'object' && typeof x !=='function' || x === null) resolve(x)
}
// resolve 静态方法
static resolve (value) {
// 如果传入 MyPromise 就直接返回
if (value instanceof MyPromise) return value
// 普通值,创建promise对象
return new MyPromise(resolve => resolve(value))
}
// reject 静态方法
static reject (reason) {
return new MyPromise((resolve, reject) => {
reject(reason)
})
}
// 工具函数, 判断function
isFunction(value) {
return typeof value === 'function';
}
}
// 测试
// npm install promises-aplus-tests -D
MyPromise.deferred = function() {
let result = {}
result.promise = new MyPromise(function(resolve,reject){
result.resolve = resolve
result.reject = reject
})
return result
}
module.exports = MyPromise
测试PromiseA+
- 安装
npm install promises-aplus-tests -D
- 代码中加入deferred
MyPromise.deferred = function() {
let result = {}
result.promise = new MyPromise(function(resolve,reject){
result.resolve = resolve
result.reject = reject
})
return result
}
module.exports = MyPromise
- package.json中配置启动命令
{
"name": "promise",
"version": "1.0.0",
"description": "my promise",
"main": "MyPromise.js",
"scripts": {
"test": "promises-aplus-tests myPromiseA+"
},
"author": "ITEM",
"license": "ISC",
"devDependencies": {
"promises-aplus-tests": "^2.1.2"
}
}
- 开始测试
npm run test
Promise-API
实例方法
- Promise.prototype.then()
- Promise.prototype.catch()
- Promise.prototype.finally()
静态方法
- Promise.all()
- Promise.race()
- Promise.allSettled()
- Promise.any()
- Promise.resolve()
- Promise.reject()
ES6官方Promise还有其他一些API,不在Promises/A+规范里, 下一篇文章中我们再看看怎么自己实现其他一些方法
Acknowledgments
- PromiseA+官方
- PromiseA+译
- github-then/promise
- github-es6-promise
- 从一道让我失眠的 Promise 面试题开始,深入分析 Promise 实现细节 good!
- 手写一个Promise/A+,完美通过官方872测试 good!
- 面试官:“你能手写一个 Promise 吗” good!
- Promise的源码实现(完美符合Promise/A+规范) good!
- BAT前端经典面试问题:史上最最最详细的手写Promise教程 good!
- 史上最详细手写promise
- Promise实现原理
- bilibili-Promise
- promise源码
- promise实现 es6
- 让你彻底掌握es6 Promise的八段代码