Open xxleyi opened 4 years ago
重整思路之后的又一微调版本,学到很多。现在回顾 Promise 的实现,其中涉及的点还是相当多的,列举如下:
function Promise(executor) {
let value = null
let status = PENDING
let deferHandlers = []
const fulfill = (res) => {
value = res
status = FULFILLED
deferHandlers.forEach(handle)
deferHandlers = null
}
const reject = (reason) => {
value = reason
status = REJECTED
deferHandlers.forEach(handle)
deferHandlers = null
}
const resolve = (res) => {
const then = getThen(res)
try {
if (then) {
doResolve(then.bind(res), resolve, reject)
} else {
fulfill(res)
}
} catch (ex) {
reject(ex)
}
}
const handle = (handler) => {
if (status === PENDING) deferHandlers.push(handler)
else if (status === FULFILLED) handler.onFulfilled(value)
else if (status === REJECTED) handler.onRejected(value)
}
this.defer = (onFulfilled, onRejected) => {
setTimeout(() => handle({ onFulfilled, onRejected }))
}
this.then = (onFulfilled, onRejected) => {
return new Promise(
(resolve, reject) => {
this.defer(
res => {
if (typeof onFulfilled === 'function') {
try {
resolve(onFulfilled(res))
} catch (ex) {
reject(ex)
}
} else {
resolve(res)
}
},
reason => {
if (typeof onRejected === 'function') {
try {
resolve(onRejected(reason))
} catch (ex) {
reject(ex)
}
} else {
reject(reason)
}
})
})
}
this.catch = (onRejected) => {
return this.then(null, onRejected)
}
doResolve(executor, resolve, reject)
}
const PENDING = 0
const FULFILLED = 1
const REJECTED = 2
const doResolve = (executor, resolve, reject) => {
let done = false
try {
executor(
(res) => {
if (done) return
done = true
resolve(res)
},
(reason) => {
if (done) return
done = true
reject(reason)
}
)
} catch (ex) {
if (done) return
done = true
reject(ex)
}
}
const getThen = (res) => {
try {
return res.then
} catch { }
}
new Promise(resolve => resolve('sync')).then(res => (console.log(res), 33)).then(res => console.log(res))
new Promise(resolve => setTimeout(() => resolve('async'), 0)).then(res => console.log(res))
var p = new Promise(resolve => setTimeout(() => resolve('async'), 0))
p.then(res => {
return "chainable then " + res
}).then(res => console.log(res))
p.then(res => {
return new Promise(r => setTimeout(() => r("chainable then with promise " + res)))
}).then(res => console.log(res)).then(res => console.log("should be be undefined", res))
let genObjWithThen = () => {
const val = 'objWithThen'
const p = {}
p.then = (onFulfilled) => {
console.log('then', val)
onFulfilled(val)
}
return p
}
p.then(() => genObjWithThen()).then((res) => console.log(res, 'getObjWithThen'))
var errorPromise = p.then(null).then(
a => {
console.log(a, 'after null')
a()
})
errorPromise.then().then().catch(e => console.log('' + e, 'catch after then'))
new Promise(resolve => setTimeout(() => resolve('async'), 0)).then(res => errorPromise).catch(e => console.log('' + e, 'catch after error promise'))
以上这个实现版本的功能实现相对官方实现算是比较齐全,其中比较关键的几个处理点如下:
参考:
Promise 拆解实现:
以上两块实现,分别完成了核心功能和基于核心功能的必要扩展两部分的目标。
但这其实有个致命问题,就是异步控制完全放在 resolve 中,属于强制延迟一轮,如果 resolve 本身已经被延迟执行过,这个延迟其实没必要,也不太合理。这促使我们将延迟机制放在 then 内部,这样的话,onFulfilled 的执行时机就有了两个选择:
这两种选择是竞争关系,只能选其一,因此需要为 value 的变化引入状态:PENDING 和 FULLFILLED。不同的状态下分别做不同的事情。
怎么样,是不是有点意思?
下面考虑继续完善,添加 reject 以及由此带来的状态控制机,以及支持多个 action
继续完善,支持 多个 action 和 chainable then
到现在为止,就只剩下 catch 部分没有实现了,而 catch 的实现理应很简单。但到此时,需要再进行一点处理: