详解 Promise
概述
Promise
是一个构造函数,用于创建一个新的 Promise 对象。该构造函数主要用于包装还没有添加 promise 支持的函数。
Promise(resolver : (resolve, reject) => void)
Promise
接受一个函数resolver
作为参数,包装需要执行的处理程序,当处理结果为成功时,将成功的返回值作为参数调用resolve
方法, 如果失败,则将失败原因作为参数调用reject
方法。
示例
const promise = new Promise(function (resolve, reject) {
setTimeout(() => {
// do something
if (Math.random() * 10 > 5) {
resolve({ status: 'success', data: '' })
} else {
reject(new Error('error'))
}
}, 500)
})
Promise状态
Promise 创建后,必然处于以下几种状态
pending
: 待定状态,既没有被兑现,也没有被拒绝fulfilled
: 操作成功。rejected
: 操作失败。
当状态从 pending
更新为fulfilled
或rejected
后,就再也不能变更为其他状态。
Promise 实例方法
.then(onFulfilled, onRejected)
then() 接收两个函数参数(也可以仅接收一个函数参数 onFulfilled)。
onFulfilled 函数参数,表示当 promise的状态从
pending
更新为fulfilled
时触发,并将成功的结果 value 作为onFulfilled
函数的参数。onRejected 函数参数,表示当promise的状态从
pending
更新为rejected
时触发,并将失败的原因 reason 作为onRejected
函数的参数。then() 方法返回的结果会被包装为一个新的promise实例。
.catch(onRejected)
catch() 可以相当于 .then(null, onRejected),即仅处理当promise的状态从 pending
更新为rejected
时触发。
.finally(onFinally)
表示promise的状态无论是从pengding
更新为fulfilled
或rejected
,当所有的 then() 和 catch() 执行完成后,最后会执行 finally() 的回调。
由于无法知道promise的最终状态,onFinally
回调函数不接收任何参数,它仅用于无论最终结果如何都要执行的情况。
promise
.then(function (res) {
console.log(res) // { status: 'success', data: '' }
})
.catch(function (reason) {
console.log(reason) // error: error
})
.finally(() => {
// do something
})
链式调用
使用Promise的一个优势是,可以链式调用的方式,执行多个then()
/catch()
方法。 且回调函数允许我们返回任何值,返回的值将会被包装为一个 promise实例,将值传给下一个then()
/catch()
方法。
promise
.then((res) => {
res.data = { a: 2 }
return res
})
.then((res) => {
console.log(res) // { status: 'success', data: { a: 2 } }
throw new Error('cath error')
})
.catch((reason) => {
console.log(reason) // error: cath error
})
Promise代码实现
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
const microtask = globalThis.queueMicrotask || ((cb) => setTimeout(cb, 0))
function LikePromise(resolver) {
if (typeof resolver !== 'function') {
throw new TypeError(`Promise resolver ${resolver} is not a function`)
}
this.state = PENDING
this.value = undefined
this.reason = undefined
this.fulfillQueue = []
this.rejectQueue = []
const that = this
function reject(reason) {
if (that.state === PENDING) {
that.state = REJECTED
that.reason = reason
that.rejectQueue.forEach((cb) => cb(reason))
}
}
function resolve(value) {
if (that.state === PENDING) {
that.state = FULFILLED
that.value = value
that.fulfillQueue.forEach((cb) => cb(value))
}
}
try {
resolver(resolve, reject)
} catch (e) {
reject(e)
}
}
function resolvePromise(promise, x, resolve, reject) {
if (promise === x) {
reject(new TypeError('chaining cycle'))
} else if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
let used = false
try {
const then = x.then
if (typeof then === 'function') {
then.call(
x,
(y) => {
if (used) return
used = true
resolvePromise(promise, y, resolve, reject)
},
(r) => {
if (used) return
used = true
reject(r)
},
)
} else {
if (used) return
used = true
resolve(x)
}
} catch (e) {
if (used) return
used = true
reject(e)
}
} else {
resolve(x)
}
}
LikePromise.prototype.then = function (onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : (value) => value
onRejected =
typeof onRejected === 'function'
? onRejected
: (reason) => {
throw reason
}
const that = this
const promise = new LikePromise((resolve, reject) => {
if (that.state === FULFILLED) {
microtask(() => {
try {
const x = onFulfilled(that.value)
resolvePromise(promise, x, resolve, reject)
} catch (e) {
reject(e)
}
})
} else if (that.state === REJECTED) {
microtask(() => {
try {
const x = onRejected(that.reason)
resolvePromise(promise, x, resolve, reject)
} catch (e) {
reject(e)
}
})
} else {
that.fulfillQueue.push(() => {
microtask(() => {
try {
const x = onFulfilled(that.value)
resolvePromise(promise, x, resolve, reject)
} catch (e) {
reject(e)
}
})
})
that.rejectQueue.push(() => {
microtask(() => {
try {
const x = onRejected(that.reason)
resolvePromise(promise, x, resolve, reject)
} catch (e) {
reject(e)
}
})
})
}
})
return promise
}
LikePromise.prototype.catch = function (onRejected) {
return this.then(null, onRejected)
}
LikePromise.prototype.finally = function (onFinally) {
return this.then(
(value) => LikePromise.resolve(onFinally()).then(() => value),
(reason) =>
LikePromise.resolve(onFinally()).then(() => {
throw reason
}),
)
}
LikePromise.resolve = function (value) {
return new LikePromise((resolve) => resolve(value))
}
LikePromise.reject = function (reason) {
return new LikePromise((_, reject) => reject(reason))
}
Promise
静态方法
Promise.resolve(value)
返回一个状态由给定value决定的Promise对象。如果该值是thenable(即,带有then方法的对象), 返回的Promise对象的最终状态由then方法执行决定;否则的话(该value为空,基本类型或者不带then方法的对象), 返回的Promise对象状态为fulfilled,并且将该value传递给对应的then方法。 通常而言,如果您不知道一个值是否是Promise对象,使用Promise.resolve(value) 来返回一个Promise对象, 这样就能将该value以Promise对象形式使用。
Promise.reject(reason)
返回一个状态为失败的Promise对象,并将给定的失败信息传递给对应的处理方法
Promise.all(promises)
all()
允许传入一组promise实例,并返回一个新的promise实例。
promises并发执行,并且当这组promises的最终状态均更新为fulfilled
时,才触发返回的promise实例的onFulfilled
, 并将这组promises的执行结果,已promises的定义顺序,以数组的形式传给onFulfilled
。 如果其中某个promise的最终状态更新为rejected
,则立即触发返回的promise实例的onRejected
。
示例
const promises = [
Promise.resolve({ a: 1 }),
new Promise((resolve) => {
setTimeout(() => {
resolve({ b: 1 })
}, 0)
}),
]
Promise.all(promises).then((res) => {
console.log(res) // [ { a: 1}, { b: 1 } ]
})
手写Promise.all 实现代码
function promiseAll(promises) {
promises = promises || []
let length = promises.length
if (length === 0) return Promise.resolve([])
let count = 0
const list = []
return new Promise((resolve, reject) => {
const resolveFn = (res, index) => {
list[index] = res
count++
if (count >= length) {
resolve(list)
}
}
promises.forEach((item, i) => {
if (item instanceof Promise) {
item.then((res) => resolveFn(res, i), reject)
} else {
resolveFn(item, i)
}
})
})
}
Promise.allSettled
allSettled(promises)
允许传入一组promise实例,并返回一个新的promise对象。
当这组promises的状态从pending
都更新到最终状态、无论最终状态是 fulfilled
或rejected
时,触发返回的promise的onfulfilled
。
onfulfilled
回调函数,根据promises定义的顺序,将执行结果以 { status: string, [value|reason]: any }[]
的形式作为参数传入。
示例
const promises = [Promise.resolve({ a: 1 }), Promise.reject('reason')]
Promise.allSettled(promises).then((res) => {
console.log(res) // [ { status: 'fulfilled’, value: { a: 1 } }, { status: 'rejected', reason: 'reason' } ]
})
手写Promise.allSettled 实现代码
function promiseAllSettled(promises) {
promises = promises || []
let length = promises.length
if (length === 0) return Promise.resolve([])
let count = 0
const list = []
return new Promise((resolve) => {
const resolveFn = (res, index, status) => {
list[index] = { status }
if (status === 'fulfilled') {
list[index].value = res
} else {
list[index].reason = res
}
count++
if (count >= length) {
resolve(list)
}
}
promises.forEach((item, i) => {
if (item instanceof Promise) {
item.then(
(res) => resolveFn(res, i, 'fulfilled'),
(reason) => resolveFn(reason, i, 'rejected'),
)
} else {
resolveFn(item, i, 'fulfilled')
}
})
})
}
Promise.race
Promise.race(promises)
接收一组promise实例作为参数,并返回一个新的promise对象。
当这组promises中的任意一个promise的状态从pending
更新为fulfilled
或rejected
时,返回的promise对象将会把该promise的成功返回值或者失败原因 作为参数调用返回的promise的onFulfilled
或onRejected
示例
const promises = [
new Promise((resolve) => {
setTimeout(() => {
resolve('timeout')
}, 500)
}),
Promise.resolve('resolve'),
]
Promise.race(promises).then((res) => {
console.log(res) // resolve
})
手写Promise.race 实现代码
function promiseRace(array) {
array = array || []
return new Promise((resolve, reject) => {
array.forEach((item) => {
if (item instanceof Promise) {
item.then(resolve, reject)
} else {
resolve(item)
}
})
})
}