Promise 雏形
Promise 其实就是一个构造函数。
它只有一个参数,按照 Promise/A+ 规范的命名,我们把 Promise 构造函数的参数叫做 executor
,它是函数类型的参数。
这个函数又“自动”具有 resolve
、reject
两个方法作为参数。
function Promise(executor) {
}
Promise 构造函数返回一个 Promise 对象实例,这个返回的 Promise 对象具有一个 then
方法。
在 then
方法中,调用者可以定义两个参数,分别是 onfulfilled
和 onrejected
,它们都是函数类型参数。
其中,onfulfilled
通过参数可以获取 Promise 对象经过 resolve
处理后的值,onrejected
可以获取 Promise 对象经过 reject
处理后的值。
通过这个值,我们来处理异步操作完成后的逻辑。
这些都是 Promise/A+ 规范的基本内容,接下来我们继续实现 Promise。
- Promise 构造函数
- 在构造函数基础上加上
then
原型方法
function Promise(executor) {
}
Promise.prototype.then = function (onfulfilled, onrejected) {
}
下面是一个示例:
let promise1 = new Promise((resolve, reject) => {
resolve('data')
})
promise1.then(data => {
console.log(data)
})
let promise2 = new Promise((resolve, reject) => {
reject('error')
})
promise2.then(data => {
console.log(data)
}, error => {
console.log(error)
})
在使用 new
关键字调用 Promise 构造函数时,在合适的时机(往往是异步操作结束时)调用 executor
的参数 resolve
,并将经过处理后的值作为 resolve
的函数参数执行,这个值便可以放在后续 then
方法的第一个函数参数 onfulfilled
中拿到。
同理,在出现错误时,调用 executor
的参数 reject
,并将错误信息作为 reject
的函数参数执行,这个错误信息可以在后续 then
方法的第二个函数参数 onrejected
中拿到。
因此,我们在实现 Promise 时,应该有两个变量,分别存储经过 resolve
处理后的值,以及经过 reject
处理后的值
同时还需要一个变量来存储状态,这个状态就是 Promise 实例的状态(pending
, fulfilled
, rejected
)
最后需要提供 resolve
方法和 reject
方法,这两个方法需要作为 executor
的参数提供给开发者使用
如下:
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
function Promise(executor) {
const self = this
this.status = PENDING
this.value = null
this.reason = null
function resolve(value) {
self.value = value
}
function reject(reason) {
self.reason = reason
}
executor(resolve, reject)
}
Promise.prototype.then = function (onfulfilled = Function.prototype, onrejected = Function.prototype) {
onfulfilled(this.value)
onrejected(this.reason)
}
为了保证 onfulfilled
、 onrejected
的健壮性,我们为它们设置了默认值,其默认值为一个函数元(Function.prototype
)
因为 resolve
的最终调用是由开发者在不确定环境下(往往是在全局中)直接调用的,因此为了在 resolve
函数中能够拿到 Promise 实例的值,我们需要对 this
另存,或者可以使用箭头函数来保证 this
执行的准确性
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
function Promise(executor) {
this.status = PENDING
this.value = null
this.reason = null
const resolve = value => {
this.value = value
}
const reject = reason => {
this.reason = reason
}
executor(resolve, reject)
}
Promise.prototype.then = function (onfulfilled = Function.prototype, onrejected = Function.prototype) {
onfulfilled(this.value)
onrejected(this.reason)
}
为什么 then
要放在 Promise 构造函数的原型上,而不是放在构造函数内部呢?
每个 Promise 实例的 then
方法逻辑都是一致的,实例在调用该方法时,可以通过原型 (Promise.proptype
) 来调用,而不需要每次实例化都新创建一个 then
方法,以便节省内存
Promise 完善状态
判断以下代码的输出:
let promise = new Promise((resolve, reject) => {
resolve('data')
reject('error')
})
promise.then(data => {
console.log(data)
}, error => {
console.log(error)
})
正常的话,以上代码只会输出 data
我们知道,Promise 实例的状态只能从 pending
变为 fulfilled
或者从 pending
变为 rejected
状态一旦改变完成,就不可再次变化或逆转
而我们上面的代码实现显然无法满足这一特性,执行上一段代码回输出 data
和 error
,因此需要对状态进行判断和完善
如下:
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
function Promise(executor) {
this.status = PENDING
this.value = null
this.reason = null
const resolve = value => {
if (this.status === PENDING) {
this.value = value
this.status = FULFILLED
}
}
const reject = reason => {
if (this.status === PENDING) {
this.reason = reason
this.status = REJECTED
}
}
executor(resolve, reject)
}
Promise.prototype.then = function (onfulfilled, onrejected) {
onfulfilled = typeof onfulfilled === 'function' ? onfulfilled : data => data
onrejected = typeof onrejected === 'function' ? onrejected : error => { throw error }
if(this.status === FULFILLED)
onfulfilled(this.value)
if(this.status === REJECTED)
onrejected(this.reason)
}
在 resolve
和 reject
方法中加入了判断,只允许 Promise 实例状态从 pending
变为 fulfilled
或者从 pending
变为 rejected
对 Promise.proptype.then
的参数 onfulfilled
和 onrejected
进行了判断,当实参不是函数类型时,就需要赋予默认函数值
这样一来,上面的示例代码就可以顺利执行了
但是,Promise 时用来解决异步问题的,而我们的代码全部都是同步执行的,似乎还差了更重要的逻辑。
Promise 异步实现
我们从下面的示例代码,逐步分析
let promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('data')
}, 1000);
})
promise.then(data => {
console.log(data)
})
正常来讲,上面代码应该在 1s
后输出 data
,但是现在,代码并没有输出任何信息。这是为什么?
原因很简单,因为我们的实现逻辑都是同步的。上面代码在实例化 Promise 的构造函数时,会在 setTimeout
逻辑中调用 resolve
,也就是说在 1s
后才会调用 resolve
方法,更改 Promise 实例状态。而结合我们的实现,then
方法中的 onfulfilled
是同步执行的,它在执行时 this.status
仍是 pending
,并没有做到 “ 1s
后再执行 onfulfilled
”
我们应该在合适的时间去调用 onfulfilled
方法,这个合适的时间应该是开发者调用 resolve
的时刻,那么我们先在 status
为 pending
时把开发者传进来的 onfulfilled
方法存起来,再在 resolve
方法中执行即可
如下:
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
function Promise(executor) {
this.status = PENDING
this.value = null
this.reason = null
this.onFulfilledFunc = Function.prototype
this.onRejectedFunc = Function.prototype
const resolve = value => {
if (this.status === PENDING) {
this.value = value
this.status = FULFILLED
this.onFulfilledFunc(this.value)
}
}
const reject = reason => {
if (this.status === PENDING) {
this.reason = reason
this.status = REJECTED
this.onRejectedFunc(this.reason)
}
}
executor(resolve, reject)
}
Promise.prototype.then = function (onfulfilled, onrejected) {
onfulfilled = typeof onfulfilled === 'function' ? onfulfilled : data => data
onrejected = typeof onrejected === 'function' ? onrejected : error => { throw error }
if (this.status === FULFILLED) {
onfulfilled(this.value)
}
if (this.status === REJECTED) {
onrejected(this.reason)
}
if (this.status === PENDING) {
this.onFulfilledFunc = onfulfilled
this.onRejectedFunc = onrejected
}
}
通过测试发现,我们实现的代码可以支持异步执行了。
我们知道 Promise 的 then
方法是异步执行的。再看一个?
let promise = new Promise((resolve, reject) => {
resolve('data')
})
promise.then(data => {
console.log(data)
})
console.log(1)
正常的话,上面的例子回按照顺序先输出 1
,再输出 data
而我们实现的代码没有考虑这种情况,实际先输出了 data
后输出 1
。因此,需要将 resolve
和 reject
的执行放到任务队列中。这里我们姑且将它们放到 setTimeout
中,保证异步执行(这样的做法并不严谨,为了保证 Promise 属于微任务,很多 Promise 的实现库用了 MutationObserver
来模仿 nextTick
)
const resolve = value => {
if (value instanceof Promise) {
return value.then(resolve, reject)
}
setTimeout(() => {
if (this.status === PENDING) {
this.value = value
this.status = FULFILLED
this.onFulfilledFunc(this.value)
}
}, 0);
}
const reject = reason => {
setTimeout(() => {
if (this.status === PENDING) {
this.reason = reason
this.status = REJECTED
this.onRejectedFunc(this.reason)
}
}, 0);
}
executor(resolve, reject)
这样一来,在执行到 executor(resolve, reject)
时,也能保证在 nextTick
中才会执行 Promise 被决议后的任务,不会阻塞同步任务
同时,我们在 resovle
方法中加入了对 value
值是否为一个 Promise 实例的判断语句
下面是目前的完整代码:
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
function Promise(executor) {
this.status = PENDING
this.value = null
this.reason = null
this.onFulfilledFunc = Function.prototype
this.onRejectedFunc = Function.prototype
const resolve = value => {
if (value instanceof Promise) {
return value.then(resolve, reject)
}
setTimeout(() => {
if (this.status === PENDING) {
this.value = value
this.status = FULFILLED
this.onFulfilledFunc(this.value)
}
}, 0);
}
const reject = reason => {
setTimeout(() => {
if (this.status === PENDING) {
this.reason = reason
this.status = REJECTED
this.onRejectedFunc(this.reason)
}
}, 0);
}
executor(resolve, reject)
}
Promise.prototype.then = function (onfulfilled, onrejected) {
onfulfilled = typeof onfulfilled === 'function' ? onfulfilled : data => data
onrejected = typeof onrejected === 'function' ? onrejected : error => { throw error }
if (this.status === FULFILLED) {
onfulfilled(this.value)
}
if (this.status === REJECTED) {
onrejected(this.reason)
}
if (this.status === PENDING) {
this.onFulfilledFunc = onfulfilled
this.onRejectedFunc = onrejected
}
}
Promise 细节完善
在 Promise 实例状态变更之前添加多个 then
方法
let promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('data')
}, 1000);
})
promise.then(data => {
console.log(`1: ${data}`)
})
promise.then(data => {
console.log(`2: ${data}`)
})
正常情况,上面的代码回输出
1: data
2: data
而我们的实现只会输出 2: data
,这是因为第二个 then
方法中的 onFulfilledFunc
会覆盖第一个 then
方法中的 onFulfilledFunc
为了解决这个问题,只需要将所有 then
方法中的 onFulfilledFunc
存储到一个数组 onFulfilledArray
中,在当前 Promise 被决议时依次执行 onFulfilledArray
数组内的方法即可。对于 onRejectedFunc
同理
改动后的代码:
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
function Promise(executor) {
this.status = PENDING
this.value = null
this.reason = null
this.onFulfilledArray = []
this.onRejectedArray = []
const resolve = value => {
if (value instanceof Promise) {
return value.then(resolve, reject)
}
setTimeout(() => {
if (this.status === PENDING) {
this.value = value
this.status = FULFILLED
this.onFulfilledArray.forEach(func => {
func(value)
})
}
}, 0);
}
const reject = reason => {
setTimeout(() => {
if (this.status === PENDING) {
this.reason = reason
this.status = REJECTED
this.onRejectedArray.forEach(func => {
func(reason)
})
}
}, 0);
}
executor(resolve, reject)
}
Promise.prototype.then = function (onfulfilled, onrejected) {
onfulfilled = typeof onfulfilled === 'function' ? onfulfilled : data => data
onrejected = typeof onrejected === 'function' ? onrejected : error => { throw error }
if (this.status === FULFILLED) {
onfulfilled(this.value)
}
if (this.status === REJECTED) {
onrejected(this.reason)
}
if (this.status === PENDING) {
this.onFulfilledArray.push(onfulfilled)
this.onRejectedArray.push(onrejected)
}
}
另一个需要完善的细节,在构造函数中如果出错,将会自动出发 Promise 实例状态为 rejected
因此我们用 try...catch
块对 executor
进行包裹
try {
executor(resolve, reject)
} catch (e) {
reject(e)
}
Promise 链式调用
先看一道题目:请判断以下代码的输出结果
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('andy')
}, 1000);
})
promise.then(data => {
console.log(data)
return `${data} next then`
}).then(data => {
console.log(data)
})
以上代码会在 1s
后输出 andy
,紧接着输出 andy next then
我们看到,Promise 实例的 then
方法支持链式调用,输出经过 resolve
处理的值后,如果在 then
方法体的 onfulfilled
函数中同步显式返回新的值,则将会在新 Promise 实例 then
方法的 onfulfilled
函数中拿到新的值
如果在第一个 then
方法体的 onfulfilled
函数中返回另一个 Promise 实例,结果又会如何
看下面的代码:
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('andy')
}, 1000);
})
promise.then(data => {
console.log(data)
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(`${data} next then`)
}, 2000);
})
}).then(data => {
console.log(data)
})
上面代码会在 1s
后输出 andy
紧接再过 2s
输出 andy next then
所以,一个 Promise 实例 then
方法的 onfulfilled
函数和 onrejected
函数是支持再次返回一个 Promise 实例的,也支持返回一个非 Promise 实例的普通值
并且,返回的 Promise 实例或非 Promise 实例的普通值将会传给下一个 then
方法的 onfulfilled
函数或 onrejected
函数
链式调用的初步实现
为了能够支持 then
方法的链式调用,每一个 then
方法的 onfulfilled
函数和 onrejected
函数都应该返回一个 Promise 实例
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
function Promise(executor) {
this.status = PENDING
this.value = null
this.reason = null
this.onFulfilledArray = []
this.onRejectedArray = []
const resolve = value => {
if (value instanceof Promise) {
return value.then(resolve, reject)
}
setTimeout(() => {
if (this.status === PENDING) {
this.value = value
this.status = FULFILLED
this.onFulfilledArray.forEach(func => {
func(value)
})
}
}, 0);
}
const reject = reason => {
setTimeout(() => {
if (this.status === PENDING) {
this.reason = reason
this.status = REJECTED
this.onRejectedArray.forEach(func => {
func(reason)
})
}
}, 0);
}
try {
executor(resolve, reject)
} catch (e) {
reject(e)
}
}
Promise.prototype.then = function (onfulfilled, onrejected) {
onfulfilled = typeof onfulfilled === 'function' ? onfulfilled : data => data
onrejected = typeof onrejected === 'function' ? onrejected : error => { throw error }
let promise2
if (this.status === FULFILLED) {
return promise2 = new Promise((resolve, reject) => {
setTimeout(() => {
try {
let result = onfulfilled(this.value)
resolve(result)
} catch (e) {
reject(e)
}
}, 0);
})
}
if (this.status === REJECTED) {
return promise2 = new Promise((resolve, reject) => {
setTimeout(() => {
try {
let result = onrejected(this.reason)
resolve(result)
} catch (e) {
reject(e)
}
}, 0);
})
}
if (this.status === PENDING) {
return promise2 = new Promise((resolve, reject) => {
this.onFulfilledArray.push(() => {
try {
let result = onfulfilled(this.value)
resolve(result)
} catch (e) {
reject(e)
}
})
this.onRejectedArray.push(() => {
try {
let result = onrejected(this.reason)
resolve(result)
} catch (e) {
reject(e)
}
})
})
}
}
这里要重点理解 this.status === PENDING
判断分支中的逻辑,这也是最难理解的。当使用 Promise 实例调用其 then
方法时,应该返回一个 Promise 实例,返回的就是 this.status === PENDING
判断分支中返回的 promise2
。那么这个 promise2
什么时候被决议呢?应该是在异步处理结束后,依次执行 onFulfilledArray
或 onRejectedArray
数组中的函数时
onFulfilledArray
和 onRejectedArray
数组中的函数应该做些什么呢?
需要切换 promise2
的状态,并进行决议
链式调用的完善实现
我们继续实现 then
方法,以便显式返回一个 Promise 实例。
如下:
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('andy')
}, 1000);
})
promise.then(data => {
console.log(data)
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(`${data} next then`)
}, 2000);
})
}).then(data => {
console.log(data)
})
基于第一种 onfulfilled
函数和 onrejected
函数返回一个普通值的情况,要实现这种 onfulfilled
函数和 onrejected
函数返回一个 Promise 实例的情况并不困难。但是需要小幅重构一下代码,在之前实现的 let result = onfulfilled(this.value)
语句和 let result = onrejected(this.reason)
语句中,使变量 result
由一个普通值变为一个 Promise 实例。
变量 result
既可以是一个普通值,也可以是一个 Promise 实例,为此我们抽象出 resulvePromise
方法进行统一处理。对已有实现进行改动后的代码。如下:
const resolvePromise = (promise2, result, resolve, reject) => {
}
Promise.prototype.then = function (onfulfilled, onrejected) {
onfulfilled = typeof onfulfilled === 'function' ? onfulfilled : data => data
onrejected = typeof onrejected === 'function' ? onrejected : error => { throw error }
let promise2
if (this.status === FULFILLED) {
return promise2 = new Promise((resolve, reject) => {
setTimeout(() => {
try {
let result = onfulfilled(this.value)
resolvePromise(promise2, result, resolve, reject)
} catch (e) {
reject(e)
}
}, 0);
})
}
if (this.status === REJECTED) {
return promise2 = new Promise((resolve, reject) => {
setTimeout(() => {
try {
let result = onrejected(this.reason)
resolvePromise(promise2, result, resolve, reject)
} catch (e) {
reject(e)
}
}, 0);
})
}
if (this.status === PENDING) {
return promise2 = new Promise((resolve, reject) => {
this.onFulfilledArray.push(() => {
try {
let result = onfulfilled(this.value)
resolvePromise(promise2, result, resolve, reject)
} catch (e) {
reject(e)
}
})
this.onRejectedArray.push(() => {
try {
let result = onrejected(this.reason)
resolvePromise(promise2, result, resolve, reject)
} catch (e) {
reject(e)
}
})
})
}
}
现在的任务就是实现 resolvePromise
函数,这个函数接收 4 个参数:
promise2
:返回的 Promise 实例result
:onfulfilled
或onrejected
函数的返回值resolve
:promise2
的resolve
方法reject
:promise2
的reject
方法
const resolvePromise = (promise2, result, resolve, reject) => {
// 当 result 和 promise 相等时,也就是在 onfulfilled 返回 promise2 时,执行 reject
if (result === promise2) {
reject(new TypeError('error due to circular reference'))
}
// 是否以及执行过 onfulfilled 或 onrejected
let consumed = false
let thenable
if (result instanceof Promise) {
if (result.status === PENDING) {
result.then((data) => {
resolvePromise(promise2, data, resolve, reject)
}, reject)
} else {
result.then(resolve, reject)
}
return
}
let isComplexResult = (target => typeof target === 'function' || typeof target === 'object') && target !== null
// 如果返回的时疑似 Promise 类型
if (isComplexResult(result)) {
try {
thenable = result.then
// 判断返回值是否是 Promise 类型
if (typeof thenable === 'function') {
thenable.call(result, function (data) {
if (consumed) {
return
}
consumed = true
return resolvePromise(promise2, data, resolve, reject)
}, function (error) {
if (consumed) {
return
}
consumed = true
return reject(error)
})
} else {
resolve(result)
}
} catch (e) {
if (consumed) {
return
}
consumed = true
return reject(e)
}
} else {
resolve(result)
}
}
resolvePromise
方法的第一步是对 “死循环” 进行处理,并在发生死循环时抛出错误
怎么理解这个处理呢?Promise 规范中指出,其实出现 “死循环” 的情况如下:
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('andy')
}, 1000);
})
promise.then(onfulfilled = data => {
console.log(data)
return onfulfilled(data)
}).then(data => {
console.log(data)
})
对于 onfulfilled
函数返回的结果 result
:如果 result
不是 Promise 实例,不是对象,也不是函数,而是一个普通值(isCompexResult
函数用于对此进行判断),则直接对 promise2
进行决议
对于 onfulfilled
函数返回的结果 result
:如果 result
含有 then
属性方法,那么我们称该属性方法为 thenable
,说明 result
是一个 Promise 实例,当执行该实例的 then
方法(即 thenable
)时,返回结果还可能是一个 Promise 实例类型,也可能是一个普通值,因此还要递归调用 resolvePromise
。
如下:
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('andy')
}, 1000);
})
promise.then(data => {
console.log(data)
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(`${data} next then`)
}, 2000);
}).then(data => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(`${data} next then`)
}, 2000);
})
})
}).then(data => {
console.log(data)
})
上面代码会在 1s
后输出 andy
,再在 5s
后输出 andy next then next then
现在的完整代码:
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
function Promise(executor) {
this.status = PENDING
this.value = null
this.reason = null
this.onFulfilledArray = []
this.onRejectedArray = []
const resolve = value => {
if (value instanceof Promise) {
return value.then(resolve, reject)
}
setTimeout(() => {
if (this.status === PENDING) {
this.value = value
this.status = FULFILLED
this.onFulfilledArray.forEach(func => {
func(value)
})
}
}, 0);
}
const reject = reason => {
setTimeout(() => {
if (this.status === PENDING) {
this.reason = reason
this.status = REJECTED
this.onRejectedArray.forEach(func => {
func(reason)
})
}
}, 0);
}
try {
executor(resolve, reject)
} catch (e) {
reject(e)
}
}
const resolvePromise = (promise2, result, resolve, reject) => {
// 当 result 和 promise 相等时,也就是在 onfulfilled 返回 promise2 时,执行 reject
if (result === promise2) {
reject(new TypeError('error due to circular reference'))
}
// 是否以及执行过 onfulfilled 或 onrejected
let consumed = false
let thenable
if (result instanceof Promise) {
if (result.status === PENDING) {
result.then((data) => {
resolvePromise(promise2, data, resolve, reject)
}, reject)
} else {
result.then(resolve, reject)
}
return
}
let isComplexResult = (target => typeof target === 'function' || typeof target === 'object') && target !== null
// 如果返回的时疑似 Promise 类型
if (isComplexResult(result)) {
try {
thenable = result.then
// 判断返回值是否是 Promise 类型
if (typeof thenable === 'function') {
thenable.call(result, function (data) {
if (consumed) {
return
}
consumed = true
return resolvePromise(promise2, data, resolve, reject)
}, function (error) {
if (consumed) {
return
}
consumed = true
return reject(error)
})
} else {
resolve(result)
}
} catch (e) {
if (consumed) {
return
}
consumed = true
return reject(e)
}
} else {
resolve(result)
}
}
Promise.prototype.then = function (onfulfilled, onrejected) {
onfulfilled = typeof onfulfilled === 'function' ? onfulfilled : data => data
onrejected = typeof onrejected === 'function' ? onrejected : error => { throw error }
let promise2
if (this.status === FULFILLED) {
return promise2 = new Promise((resolve, reject) => {
setTimeout(() => {
try {
let result = onfulfilled(this.value)
resolvePromise(promise2, result, resolve, reject)
} catch (e) {
reject(e)
}
}, 0);
})
}
if (this.status === REJECTED) {
return promise2 = new Promise((resolve, reject) => {
setTimeout(() => {
try {
let result = onrejected(this.reason)
resolvePromise(promise2, result, resolve, reject)
} catch (e) {
reject(e)
}
}, 0);
})
}
if (this.status === PENDING) {
return promise2 = new Promise((resolve, reject) => {
this.onFulfilledArray.push(() => {
try {
let result = onfulfilled(this.value)
resolvePromise(promise2, result, resolve, reject)
} catch (e) {
reject(e)
}
})
this.onRejectedArray.push(() => {
try {
let result = onrejected(this.reason)
resolvePromise(promise2, result, resolve, reject)
} catch (e) {
reject(e)
}
})
})
}
}
Promise 穿透实现
到这里,除了静态方法,我们的 Promise 实现基本已经完成了 95%
为什么不是 100% 呢?还有一处细节没有完成,我们来看下面的代码,判断输出结果
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('andy')
}, 1000);
})
promise.then(null).then(data => {
console.log(data)
})
上面的代码会在 1s
后输出 andy
。
这就是 Promise 穿透现象:给 then()
函数传递非函数值作为其参数时,实际上会被解析成 then(null)
,这时,上一个 Promise 对象的决议结果便会 “穿透” 到下一个 then
方法的参数中
如何实现 Promise 穿透
其实很简单,并且已经实现。在 then()
方法的实现中,我们已经为 onfulfilled
和 onrejected
函数加上了如下判断:
Promise.prototype.then = function (onfulfilled, onrejected) {
onfulfilled = typeof onfulfilled === 'function' ? onfulfilled : data => data
onrejected = typeof onrejected === 'function' ? onrejected : error => { throw error }
// ...
}
如果 onfulfilled
不是函数类型,则给一个默认值,该默认值是返回其参数的函数。onreject
函数同理。这段代码就实现了 “穿透” 的逻辑
Promise 静态方法和其他方法
- Promise.proptype.catch
- Promise.resolve
- Promise.reject
- Promise.all
- Promise.race
Promise.prototype.catch 实现
Promise.prototype.catch
可以用来进行异常捕获。用法如下:
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
reject('error')
}, 1000);
})
promise.then(data => {
console.log(data)
}).catch(error => {
console.log(error)
})
上面代码会在 1s
后输出 error
其实在这种场景下,它与以下代码是等价的
Promise.prototype.catch = function (catchFunc) {
return this.then(null, catchFunc)
}
因为我们知道 then()
方法的第二个参数也是进行异常捕获的,所以通过这个特性,我们可以比较简单的实现 Promise.prototype.catch
Promise.resolve 实现
?:
Promise.resolve('data').then(data => {
console.log(data)
})
console.log(1)
上面代码会先输出 1
,再输出 data
实现:
Promise.resolve = function (value) {
return new Promise((resolve, reject) => {
resolve(value)
})
}
顺带实现 Promise.reject(reason)
Promise.reject = function (reason) {
return new Promise((resolve, reject) => {
reject(reason)
})
}
Promise.all 实现
?:
let promise1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('andy')
}, 1000)
})
let promise2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('andy')
}, 1000)
})
Promise.all([promise1, promise2]).then(data => {
console.log(data)
})
上面代码会在 1s
后输出 ["andy", "andy"]
实现:
Promise.all = (promiseArray) => {
if (!Array.isArray(promiseArray)) {
throw new TypeError('The arguments should be an array!')
}
return new Promise((resolve, reject) => {
try {
let resultArray = []
const len = promiseArray.length
if (len === 0) {
return resolve(resultArray)
}
let count = 0
for (let i = 0;i < len;i++) {
Promise.resolve(promiseArray[i]).then(data => {
count++
resultArray[i] = data
if (count === len) {
resolve(resultArray)
}
}, reject)
}
} catch (e) {
reject(e)
}
})
}
我们先对参数 promiseArray
的类型进行判断,如果是非数组类型参数则抛出错误。Promise.all
会返回一个 Promise 实例,这个实例将会在 promiseArray
中的所有 Promise 实例被决议后进行决议,决议结果是一个数组,这个数组存有 promiseArray
中的所有 Promise 实例的决议值
Promise.race 实现
?:
let promise1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('andy1')
}, 1000)
})
let promise2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('andy2')
}, 2000)
})
Promise.race([promise1, promise2]).then(data => {
console.log(data)
})
上面代码会在 1s
后输出 andy1
实现:
Promise.race = (promiseArray) => {
if (!Array.isArray(promiseArray)) {
throw new TypeError('The arguments should be an array!')
}
return new Promise((resolve, reject) => {
try {
const len = promiseArray.length
if (len === 0) {
return resolve()
}
for (let p of promiseArray) {
Promise.resolve(p).then(resolve, reject)
}
} catch (e) {
reject(e)
}
})
}
同步执行 promiseArray
数组中所有 Promise 实例的 then
方法,第一个 resolve
的示例会直接触发新的 Promise 实例
总结
Promise/A+
Promise
的状态一经改变就不能再改变。(见3.1).then
和.catch
都会返回一个新的Promise
。catch
不管被连接到哪里,都能捕获上层的错误。(见3.2)- 在
Promise
中,返回任意一个非promise
的值都会被包裹成promise
对象,例如return 2
会被包装为return Promise.resolve(2)
。 Promise
的.then
或者.catch
可以被调用多次, 当如果Promise
内部的状态一经改变,并且有了一个值,那么后续每次调用.then
或者.catch
的时候都会直接拿到该值。(见3.5).then
或者.catch
中return
一个error
对象并不会抛出错误,所以不会被后续的.catch
捕获。(见3.6).then
或.catch
返回的值不能是promise
本身,否则会造成死循环。(见3.7).then
或者.catch
的参数期望是函数,传入非函数则会发生值穿透。(见3.8).then
方法是能接收两个参数的,第一个是处理成功的函数,第二个是处理失败的函数,再某些时候你可以认为catch
是.then
第二个参数的简便写法。(见3.9).finally
方法也是返回一个Promise
,他在Promise
结束的时候,无论结果为resolved
还是rejected
,都会执行里面的回调函数。.finally()
方法不管Promise
对象最后的状态如何都会执行.finally()
方法的回调函数不接收任何参数,也就是说你在.finally()
函数中是无法知道Promise
的最终的状态- 它最终返回的是上一次的 Promise 对象值,不过如果抛出的是一个异常则返回异常的
Promise
对象 Promise.all()
的作用是接收一组异步任务,然后并行执行异步任务,并且在所有异步操作执行完成后才执行回调.race()
的作用也是接收一组异步任务,然后并行执行异步任务,只保留第一个执行完成的异步操作的结果,其他的方法仍在执行,不过执行结果会被抛弃Promise.all().then()
结果中数组的顺序和Promise.all()
接收的数组顺序一致all
和race
传入的数组中如果有会抛出异常的异步任务,那么只有最先抛出的错误会被捕获,并且是被then
的第二个参数或者后面的catch
捕获;但并不会影响数组中其他的异步任务的执行
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!