Skip to content

Instantly share code, notes, and snippets.

@lovetingyuan
Last active November 17, 2020 02:25
Show Gist options
  • Save lovetingyuan/f52aeb1a1912d49675f082d6945fef1b to your computer and use it in GitHub Desktop.
Save lovetingyuan/f52aeb1a1912d49675f082d6945fef1b to your computer and use it in GitHub Desktop.
promise polyfill
function resolveValue (promise, value, resolve, reject) {
if (promise === value) { // 不能resolve自身
return reject(new TypeError('Can not resolve or return the current promise.'))
}
if (value === null || (typeof value !== 'object' && typeof value !== 'function')) {
return resolve(value)
}
let then // thenable可能是对象或者函数,它的then只能读取一次并且需要捕获可能的错误
try {
then = value.then
} catch (err) {
return reject(err)
}
if (typeof then !== 'function') return resolve(value)
let called = false // 所有的回调只能调用一次
try { // 处理thenable,当然promise本身也是thenable
then.call(value, val => {
if (called) return
called = true
resolveValue(promise, val, resolve, reject) // 需要递归resolve,因为可能多次返回thenable
}, err => {
if (called) return
called = true
reject(err) // reject就直接调用即可
})
} catch (err) {
if (!called) reject(err)
}
}
function Promise (callback) {
if (!(this instanceof Promise)) throw new TypeError('Promise cannot be invoked without "new".')
if (typeof callback !== 'function') throw new TypeError('Promise callback is not a function.')
this._status = 'pending'
this._value = undefined
this._callbacks = { resolved: [], rejected: [] }
const settle = (status, value) => {
this._value = value
this._status = status
this._callbacks[status].forEach(cb => cb(value))
}
let called = false
const onResolve = value => {
if (called) return
called = true
resolveValue(this, value, val => settle('resolved', val), err => settle('rejected', err))
}
const onReject = err => {
if (called) return
called = true
settle('rejected', err)
}
try {
callback(onResolve, onReject)
} catch (err) {
onReject(err)
}
}
Promise.prototype.then = function then (onResolve, onReject) {
const handleCallback = (promise, status, resolve, reject) => {
const callback = status === 'resolved' ? onResolve : onReject
const settle = status === 'resolved' ? resolve : reject
setTimeout(() => { // then的回调需要延迟执行,实际应该放到微任务队列中
try {
if (typeof callback === 'function') {
resolveValue(promise, callback(this._value), resolve, reject)
} else {
settle(this._value)
}
} catch (err) {
reject(err)
}
})
}
let promise // then必须返回一个新的promise
if (this._status === 'pending') { // 如果是异步执行需要先把回调存储在队列中
promise = new Promise((resolve, reject) => {
this._callbacks.resolved.push(() => handleCallback(promise, 'resolved', resolve, reject))
this._callbacks.rejected.push(() => handleCallback(promise, 'rejected', resolve, reject))
})
} else {
let resolve, reject
promise = new Promise((...args) => [resolve, reject] = args)
handleCallback(promise, this._status, resolve, reject)
}
return promise
}
@lovetingyuan
Copy link
Author

符合Promise A+规范

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment