Promise
promise 表示一个异步操作的最终结果。和一个 promise 进行交互的主要方式是通过它的 then 方法,该方法注册回调要么接收一个 promise 的最终值,要么接收 promise 为什么不能被满足的原因。
规范
-
术语
promise是一个带有then方法的对象或函数,其行为符合此规范。thenable是一个定义了then方法的对象或函数。value是任何合法的JavaScript值(包括undefined、thenable或promise)。exception是使用throw语句抛出的值。reason是一个值,它指示为什么一个承诺被拒绝。
-
要求
-
promise状态-
pending:等待状态- 当处于等待状态时可以转化成
fulfilled/rejected
- 当处于等待状态时可以转化成
-
fulfilled:完成状态- 一定不能转换成其他状态
- 必须有一个不能转换的值(
value)
-
rejected:拒绝状态- 一定不能转换成其他状态
- 必须又一个不能转换的原因(
reason)
-
-
then方法:一个promise必须提供一个then方法来访问它的当前或最终值(value)或原因(reason),then方法接收两个参数promise.then(onFulfilled, onRejected)-
onFulfilled和onRejected都是可选的参数- 如果
onFulfilled不是个函数,必须被忽略 - 如果
onRejected不是个函数,必须被忽略
- 如果
-
如果
onFulfilled是一个函数- 必须在
promise完成(resolve)后被调用,promise的值(value)作为它第一个参数 - 一定不能在
promise完成(resolve)前被调用 - 一定不能被调用多次
- 必须在
-
如果
onRejected是一个函数- 必须在
promise拒绝(reject)后被调用,promise的值(reason)作为它第一个参数 - 一定不能在
promise拒绝(reject)前被调用 - 一定不能被调用多次
- 必须在
-
在执行上下文堆栈只包含平台代码
[3.1]之前,不能调用onfulfilled或onRejected -
onFulfilled和onRejected必须作为函数调用(没有this值)[3.2] -
同一个
promise上的then可能被多次调用- 如果
promise被完成(resolve),所有相应的onFulfilled需要按照then的顺序被顺序执行 - 如果
promise被拒绝(reject),所有相应的onRejected需要按照then的顺序被顺序执行
- 如果
-
then必须返回一个promise实例[3.3]promise2 = promise1.then(onFulfilled, onRejected)- 如果
onFulfilled和onRejected返回一个值x,运行[[Resolve]](promise2, x) - 如果
onFulfilled和onRejected抛出一个异常e,promise2必须要用e作为理由拒绝 - 如果
onFulfilled不是一个函数并且promise1被完成(resolve),promise2需要返回跟promise1同样的值 (value) - 如果
onRejected不是一个函数并且promise1被拒绝(reject),promise2需要返回跟promise1同样的值 (reason)
- 如果
-
-
promise解决程序([[Resolve]])[[Resolve]]是一个抽象的概念,它一个promise和x作为输入[[Resolve]](promise, x),如果x是一个thenable,它试图让承诺采用x的状态,假设x至少表现得有点像一个promise。否则,它将使用值x来实现promise,这种thenable的处理允许promise的实现更具有通用型,只要它们暴露一个遵守Promise/A+的then方法即可。它还允许遵守Promise/A+规范的实现可以与那些不太规范但是可用的实现进行共存,运行[[Resolve]](promise, x),执行以下步骤-
如果
promise和x指向同一个对象,则以TypeError作为原因拒绝promise -
如果
x是一个promise则采取它的状态[3.4]- 如果
x状态是pending,则promise必须持续pending,直到x被完成或拒绝。 - 如果
x状态是resolve,则用相同的值(value)解决promise - 如果
x状态是reject,则用相同的原因(reason)拒绝promise
- 如果
-
如果
x是一个函数或者对象- 使
then的值为x.then[3.5] - 如果获取
x.then的值抛出异常e, 则将e作为原因拒绝promise - 如果
then是一个函数,用x作为this调用它,第一个参数resolvePromise,第二个参数rejectPromise,其中- 如果当
resolvePromise的值为y时,运行[[Resolve]](promise, y) - 如果
rejectPromise用一个原因r调用,用r拒绝promise - 如果同时调用了
resolvePromise和rejectPromise,或者多次调用相同的参数,那么第一次调用优先,后续的调用将被忽略。(针对thenable) - 如果调用
then抛出一个异常e- 如果
resolvePromise或rejectPromise已经被调用,忽略它。 - 否则用
e作为原因拒绝promise
- 如果
- 如果当
- 如果
then不是一个函数,用x完成(resolve)promise
- 使
-
如果
x不是一个对象或函数,用x完成(resolve)promise
-
-
如果一个 promise 被 thenable 解析,并参与一个循环的 thenable 链,这样 [[Resolve]](promise, thenable) 的递归性质最终导致 [[Resolve]](promise, thenable) 再次被调用,按照上述算法将导致无限递归。我们鼓励(但不是必需)实现检测这种递归,并以 TypeError 作为原因拒绝承诺。[3.6]
-
注解
-
这里
平台代码使引擎、环境以及promise的实现代码。在实践中,这需要确保onFulfilled和onRejected异步地执行,并且应该在then方法被调用的那一轮事件循环之后用新的执行栈执行。这可以用如setTimeout或setImmediate这样的“宏任务”机制实现,或者用如MutationObserver或process.nextTick这样的“微任务”机制实现。由于promise的实现被考虑为平台代码,因此在自身处理程序被调用时可能已经包含一个任务调度队列 -
严格模式下,它们中的this将会是undefined;在非严格模式,this将会是全局对象
-
假如实现满足所有需求,可以允许
promise2 === promise1。每一个实现都应该记录是否能够产生promise2 === promise1以及什么情况下会出现promise2 === promise1 -
通常,只有
x来自于当前实现,才知道它是一个真正的promise。这条规则允许那些特例实现采用符合已知要求的Promise的状态 -
这个程序首先存储
x.then的引用,之后测试那个引用,然后再调用那个引用,这样避免了多次访问x.then属性。此类预防措施对于确保访问者属性的一致性非常重要,因为访问者属性的值可能在俩次检索之间发生变化 -
实现不应该在
thenable链的深度上做任意限制,并且假设超过那个任意限制将会无限递归。只有真正的循环才应该引发一个TypeError;如果遇到一个无限循环的thenable,永远执行递归是正确的行为
-
大纲图
代码实现
// ES6
// 三种状态
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise {
// 状态: 初始状态为 pending
status = PENDING
// 值
value = null
// 原因
reason = null
// 执行 onFulfilled 的队列
onFulfilledCallbacks = []
// 执行 onRejected 的队列
onRejectedCallbacks = []
// 构造方法
constructor(executor){
try {
executor(this.resolve, this.reject)
} catch (error) {
this.reject(error)
}
}
resolve = value => {
// 判断是否状态处于等待状态
if(this.status === PENDING){
// 改变状态
this.status = FULFILLED
// 赋值
this.value = value
// 循环调用
while(this.onFulfilledCallbacks.length){
this.onFulfilledCallbacks.shift()(this.value)
}
}
}
reject = reason => {
// 判断是否状态处于等待状态
if(this.status === PENDING){
// 更改状态
this.status = REJECTED
// 赋值原因
this.reason = reason
// 循环调用
while(this.onRejectedCallbacks.length){
this.onRejectedCallbacks.shift()(this.reason)
}
}
}
then(onFulfilled, onRejected){
// 可选参数
const realOnFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
const realOnRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }
const promise1 = new MyPromise((resolve, reject) => {
// 创建一个微任务执行完成的函数
const fulfilledMicrotask = () => {
queueMicrotask(() => {
try{
let x = realOnFulfilled(this.value)
resolvePromise(promise1, x, resolve, reject)
}catch (error) {
reject(error)
}
})
}
// 创建一个微任务执行拒绝的函数
const rejectMicrotask = () => {
queueMicrotask(() => {
try{
let x = realOnRejected(this.reason)
resolvePromise(promise1, x, resolve, reject)
}catch (error) {
reject(error)
}
})
}
// 状态确定后直接执行
if(this.status == FULFILLED){
fulfilledMicrotask()
}else if(this.status == REJECTED){
rejectMicrotask()
}else{
// 异步,加入队列
this.onFulfilledCallbacks.push(fulfilledMicrotask)
this.onRejectedCallbacks.push(rejectMicrotask)
}
})
// then 返回一个新的 promise
return promise1
}
// catch 方法
catch (onRejected) {
this.then(null, onRejected)
}
}
function resolvePromise(promise, x, resolve, reject){
if(x === promise){
// 循环调用,直接报错
return reject(new TypeError('The promise and the return value are the same'));
}
if(typeof x === 'function' || typeof x === 'object'){
// null 直接返回
if(x === null) return resolve(x)
let then
try {
then = x.then
} catch (error) {
// 不存在直接拒绝
return reject(error)
}
// 如果对象上面存在 then 方法
if(typeof then === 'function'){
let called = false
try {
then.call(x, y => {
// 执行多次忽略
if(called) return
called = true
// 接着执行
resolvePromise(promise, y, resolve, reject)
}, r => {
// 执行多次忽略
if(called) return
called = true
reject(r)
})
} catch (error) {
//
if(called) return
called = true
reject(error)
}
}else{
// then 不是函数
resolve(x)
}
}else{
// 如果 x 不为对象或者函数,直接用 x 为参数执行 promise
resolve(x)
}
}
// 测试用
MyPromise.deferred = function(){
var result = {}
result.promise = new MyPromise(function(resolve, reject){
result.resolve = resolve
result.reject = reject
})
return result
}
module.exports = MyPromise
// ES5
var PENDING = 'pending';
var REJECTED = 'rejected';
var FULFILLED = 'fulfilled';
function MyPromise(executor){
this.status = PENDING
this.value = null;
this.reason = null;
this.onFulfilled = []
this.onRejected = []
this.resolve = this.resolve.bind(this)
this.reject = this.reject.bind(this)
try {
executor(this.resolve, this.reject)
} catch (error) {
this.reject(error)
}
}
MyPromise.prototype.resolve = function(value){
if(this.status === PENDING){
this.status = FULFILLED
this.value = value
while(this.onFulfilled.length){
this.onFulfilled.shift()(this.value)
}
}
}
MyPromise.prototype.reject = function(reason){
if(this.status === PENDING){
this.status = REJECTED
this.reason = reason
while(this.onRejected.length){
this.onRejected.shift()(this.reason)
}
}
}
MyPromise.prototype.then = function(onFulfilled, onRejected){
var that = this
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function(value){ return value }
onRejected = typeof onRejected === 'function' ? onRejected : function(reason){ throw reason }
var promise1 = new MyPromise(function(resolve, reject){
var fulfilled = function(){
// 没有 queueMicrotask 可以用 setTimeout
queueMicrotask(function(){
try {
var x = onFulfilled(that.value)
resolvePromise(promise1, x, resolve, reject)
} catch (error) {
reject(error)
}
})
}
var rejected = function(){
queueMicrotask(function(){
try {
var x = onRejected(that.reason)
resolvePromise(promise1, x, resolve, reject)
} catch (error) {
reject(error)
}
})
}
if(that.status === FULFILLED){
fulfilled()
}else if(that.status === REJECTED){
rejected()
}else{
that.onFulfilled.push(fulfilled)
that.onRejected.push(rejected)
}
})
return promise1
}
MyPromise.prototype.catch = function(onRejected){
this.then(null, onRejected)
}