一個簡單的手寫Promise(持續完善中)
阿新 • • 發佈:2021-11-11
const PENDING = 'pending' const FULFILLED = 'fulfilled' const REJECTED = 'rejected' class MyPromise { constructor(executor) { // 捕獲執行器錯誤 try { executor(this.resolve, this.reject) } catch (error) { this.reject(error) } } // promise狀態 status = PENDING // 成功之後的值 value = undefined // 失敗之後的原因 reason = undefined successCallback = [] failCallback = [] resolve = value => { if (this.status !== PENDING) return this.status = FULFILLED this.value = value // this.successCallback && this.successCallback(this.value) while (this.successCallback.length) this.successCallback.shift()() } reject = reason => { if (this.status !== PENDING) return this.status = REJECTED this.reason = reason // this.failCallback && this.failCallback(this.reason) while (this.failCallback.length) this.failCallback.shift()() } then(successCallback, failCallback) { // 若不傳回調函式,則直接將結果返回 successCallback = successCallback ? successCallback : value => value failCallback = failCallback ? failCallback : value => { throw value } let promise = new MyPromise((resolve, reject) => { if (this.status === FULFILLED) { setTimeout(() => { handlePromise(promise, successCallback, this.value, resolve, reject) }, 0) } else if (this.status === REJECTED) { setTimeout(() => { handlePromise(promise, failCallback, this.reason, resolve, reject) }, 0) } else { this.successCallback.push(() => { setTimeout(() => { handlePromise(promise, successCallback, this.value, resolve, reject) }, 0) }) this.failCallback.push(() => { setTimeout(() => { handlePromise(promise, failCallback, this.reason, resolve, reject) }, 0) }) } }) return promise } finally(callback) { return this.then(res => { return MyPromise.resolve(callback()).then(() => res) }, err => { throw MyPromise.resolve(callback()).then(() => { throw err }) }) } catch(failCallback) { return this.then(undefined, failCallback) } static all(array) { let result = [] let index = 0 return new MyPromise((resolve, reject) => { function addResult(key, value) { result[key] = value if (++index === array.length) resolve(result) } for (let i = 0; i < array.length; i++) { const element = array[i] if (element instanceof MyPromise) { // element 是promise物件 element.then(v => addResult(i, v), e => reject(e)) } else { addResult(i, element) } } }) } static resolve(value) { if (value instanceof MyPromise) { return value } else { return new MyPromise((resolve, reject) => { resolve(value) }) } } static race(array) { return new MyPromise((resolve, reject) => { function addResult(key, value) { result[key] = value if (++index === array.length) resolve(result) } for (let i = 0; i < array.length; i++) { const element = array[i] if (element instanceof MyPromise) { // element 是promise物件 element.then(v => resolve(v), e => reject(e)) } else { resolve(element) } } }) } static allSettled(array) { let result = [] let index = 0 return new MyPromise((resolve, reject) => { function addResult(key, value) { result[key] = value if (++index === array.length) resolve(result) } for (let i = 0; i < array.length; i++) { const element = array[i] if (element instanceof MyPromise) { // element 是promise物件 element.then(v => addResult(i, { status: 'fulfilled', value: v }), e => addResult(i, { status: 'rejected', reason: e })) } else { addResult(i, { status: 'fulfilled', value: element }) } } }) } } function handlePromise(promise, callback, value, resolve, reject) { try { let x = callback(value) if (promise === x) return reject(new TypeError('Chaining cycle detected for promise #<Promise>')) if (x instanceof MyPromise) { x.then(resolve, reject) } else { resolve(x) } } catch (error) { reject(error) } } module.exports = MyPromise