Last active
November 17, 2020 02:25
-
-
Save lovetingyuan/f52aeb1a1912d49675f082d6945fef1b to your computer and use it in GitHub Desktop.
promise polyfill
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
符合Promise A+规范