createManualPromise(创建手动操作的 Promise)
源码
function createManualPromise(executor, options) {
let { abort, signal = {} } = options || {}
let aborted = signal.aborted || false
let resolve, reject, properties = {
then: { value() { return manual(Promise.prototype.then.apply(this, arguments)) } },
catch: { value() { return manual(Promise.prototype.catch.apply(this, arguments)) } },
finally: { value() { return manual(Promise.prototype.finally.apply(this, arguments)) } },
next: { value(result) { aborted || resolve(result) } },
abort: {
value(reason) {
if (aborted) return
if (reason === void 0) reason = new DOMException('Promise aborted', 'AbortError')
aborted = true, reject(reason), signal.reason = reason
if (!signal.aborted) signal.aborted = true, abort && options.abort(reason)
}
},
aborted: { get() { return aborted } },
abortedReason: { get() { return signal.reason } },
throwIfAborted: { value() { if (aborted) throw signal.reason } }
}
let manual = (target) => Object.defineProperties(target, properties)
let promise = typeof executor == 'function' ? new Promise(executor) : executor
promise = manual(Promise.race([promise, new Promise((a, b) => { resolve = a, reject = b })]))
aborted
? (aborted = false, promise.abort(signal.reason))
: signal.addEventListener && signal.addEventListener('abort', () => promise.abort(signal.reason))
return promise
}
使用示例
async function request(params) {
await new Promise(r => setTimeout(r, 5000))
if (Math.random() > 0.5) return 'success'
else throw 'fail'
}
function scene1() {
const promise = createManualPromise((resolve, reject) => {
request({ name: 'A' }).then(resolve, reject)
setTimeout(() => promise.abort('A: timeout'), 4000)
}).then(console.log, console.error)
return promise
}
function scene2() {
const promise = createManualPromise(
request({ name: 'B' })
).then(console.log, console.error)
setTimeout(() => promise.abort('B: auto abort'), 3000)
return promise
}
function scene3() {
const controller = new AbortController()
const promise = createManualPromise(
request({ name: 'C' }), { signal: controller.signal }
).then(console.log, console.error)
const button = document.createElement('button')
button.innerText = 'Abort'
button.onclick = () => controller.abort('C: user abort')
document.body.appendChild(button)
promise.then(() => button.remove())
return promise
}
function scene4() {
const promise = createManualPromise(
request({ name: 'D' })
).then(console.log, console.error)
setTimeout(() => promise.next('D: auto result'), 2000)
return promise
}
;(async () => {
await new Promise(r => setTimeout(r))
console.log('===== scene1 start =====')
await scene1()
console.log('===== scene1 end =====')
console.log('===== scene2 start =====')
await scene2()
console.log('===== scene2 end =====')
console.log('===== scene3 start =====')
await scene3()
console.log('===== scene3 end =====')
console.log('===== scene4 start =====')
await scene4()
console.log('===== scene4 end =====')
})()