Luogu3402 可持久化並查集
阿新 • • 發佈:2020-07-20
Promise
promise-a-plus
const PENDING = 'PENDING' const REJECTED = 'REJECTED' const RESOLVED = 'RESOLVED' const resolvePromise = (promise2, x, resolve, reject) => { if (promise2 === x) { // ES6 規範寫法 無法通過Promise/A+測試 // return reject('[TypeError: Chaining cycle detected for promise #<Promise>]') // Promise/A+ 規範寫法 return reject(new TypeError('Chaining cycle detected for promise #<Promise>')) } let called if ((typeof x === 'object' && x !== null) || typeof x === 'function') { try { const then = x.then if (typeof then === 'function') { then.call(x, y => { if (called) return called = true resolvePromise(promise2, y, resolve, reject) }, e => { if (called) return called = true reject(e) }) } else { resolve(x) } } catch (error) { if (called) return called = true reject(error) } } else { resolve(x) } } class Promise { constructor(executor) { if (typeof executor !== 'function') { throw new TypeError(`Promise resolver ${executor} is not a function`) } this.status = PENDING this.value = undefined this.onResolvedCallbackArr = [] this.onRejectedCallbackArr = [] const resolve = (value) => { // resolve中使用模板字串,無法通過Promise/A+測試 // console.log(`${value}`) if (value === this) { return reject(new TypeError('Chaining cycle detected for promise #<Promise>')) } if (value instanceof Promise) { return value.then(resolve, reject) } // resolve解析thenable物件是ES6的功能,無法通過Promise/A+測試 // if (((typeof value === 'object' && value !== null) || typeof value === 'function') && // typeof value.then === 'function') { // return process.nextTick(() => { // try { // value.then(resolve, reject) // } catch (error) { // reject(error) // } // }) // } if (this.status === PENDING) { this.value = value this.status = RESOLVED this.onResolvedCallbackArr.forEach(cb => cb()) } } const reject = (reason) => { if (this.status === PENDING) { this.value = reason this.status = REJECTED this.onRejectedCallbackArr.forEach(cb => cb()) } } try { executor(resolve, reject) } catch (error) { reject(error) } } then(onResolved, onRejected) { onResolved = typeof onResolved === 'function' ? onResolved : value => value onRejected = typeof onRejected === 'function' ? onRejected : error => { throw error } const promise2 = new Promise((resolve, reject) => { if (this.status === RESOLVED) { setTimeout(() => { try { const x = onResolved(this.value) resolvePromise(promise2, x, resolve, reject) } catch (error) { reject(error) } }, 0) } if (this.status === REJECTED) { setTimeout(() => { try { const x = onRejected(this.value) resolvePromise(promise2, x, resolve, reject) } catch (error) { reject(error) } }, 0) } if (this.status === PENDING) { this.onResolvedCallbackArr.push(() => { setTimeout(() => { try { const x = onResolved(this.value) resolvePromise(promise2, x, resolve, reject) } catch (error) { reject(error) } }, 0) }) this.onRejectedCallbackArr.push(() => { setTimeout(() => { try { const x = onRejected(this.value) resolvePromise(promise2, x, resolve, reject) } catch (error) { reject(error) } }, 0) }) } }) return promise2 } //==============以下非 Promise/A+ 規範中的內容================== catch(onRejected) { return this.then(null, onRejected) } finally(callback) { return this.then(value => { return Promise.resolve(callback()).then(() => value) }, error => { return Promise.resolve(callback()).then(() => { throw error }) }) } static resolve(value) { if (value instanceof Promise) return value return new Promise((resolve, reject) => { if (((typeof value === 'object' && value !== null) || typeof value === 'function') && typeof value.then === 'function') { process.nextTick(() => { try { value.then(resolve, reject) } catch (error) { reject(error) } }) } else { resolve(value) } }) } static reject(reason) { return new Promise((resolve, reject) => { reject(reason) }) } static all(promises) { return new Promise((resolve, reject) => { if (promises == undefined || !promises[Symbol.iterator]) { const preReason = promises === undefined ? `${promises}` : `${typeof promises} ${promises}` return reject(new TypeError(`${preReason} is not iterable (cannot read property Symbol(Symbol.iterator))`)) } if (promises.length === 0) return resolve([]) let index = 0 const resultArr = [] const processValue = (i, value) => { resultArr[i] = value if (++index === promises.length) { return resolve(resultArr) } } for (let i = 0; i < promises.length; i++) { Promise.resolve(promises[i]).then(value => { processValue(i, value) }, error => { return reject(error) }) } }) } static race(promises) { return new Promise((resolve, reject) => { if (promises == undefined || !promises[Symbol.iterator]) { const preReason = promises === undefined ? `${promises}` : `${typeof promises} ${promises}` return reject(new TypeError(`${preReason} is not iterable (cannot read property Symbol(Symbol.iterator))`)) } if (promises.length === 0) return for (let i = 0; i < promises.length; i++) { Promise.resolve(promises[i]).then(value => { return resolve(value) }, error => { return reject(error) }) } }) } } Promise.defer = Promise.deferred = function () { const dfd = {} dfd.promise = new Promise((resolve, reject) => { dfd.resolve = resolve dfd.reject = reject }) return dfd } module.exports = Promise
promise-es6
/** * ES6實現的Promise,在Promise/A+基礎上擴充套件了很多功能,無法通過為Promise/A+寫的測試 */ const PENDING = 'PENDING' const REJECTED = 'REJECTED' const RESOLVED = 'RESOLVED' const resolvePromise = (promise2, x, resolve, reject) => { if (promise2 === x) { // ES6 規範寫法 無法通過Promise/A+測試 return reject('[TypeError: Chaining cycle detected for promise #<Promise>]') } let called if ((typeof x === 'object' && x !== null) || typeof x === 'function') { try { const then = x.then if (typeof then === 'function') { // then.call(x, y => { // if (called) return // called = true // resolvePromise(promise2, y, resolve, reject) // }, e => { // if (called) return // called = true // reject(e) // }) // 模擬ES6的行為(非同步呼叫thenable的then方法)。無法通過Promise/A+測試 process.nextTick(() => { then.call(x, y => { if (called) return called = true resolvePromise(promise2, y, resolve, reject) }, e => { if (called) return called = true reject(e) }) }) } else { resolve(x) } } catch (error) { if (called) return called = true reject(error) } } else { resolve(x) } } class Promise { constructor(executor) { if (typeof executor !== 'function') { throw new TypeError(`Promise resolver ${executor} is not a function`) } this.status = PENDING this.value = undefined this.onResolvedCallbackArr = [] this.onRejectedCallbackArr = [] const resolve = (value) => { if (value === this) { return reject(new TypeError('Chaining cycle detected for promise #<Promise>')) } if (value instanceof Promise) { return value.then(resolve, reject) } // resolve解析thenable物件是ES6 Promise的功能,無法通過Promise/A+測試 if (((typeof value === 'object' && value !== null) || typeof value === 'function') && typeof value.then === 'function') { return process.nextTick(() => { try { value.then(resolve, reject) } catch (error) { reject(error) } }) } if (this.status === PENDING) { this.value = value this.status = RESOLVED this.onResolvedCallbackArr.forEach(cb => cb()) } } const reject = (reason) => { if (this.status === PENDING) { this.value = reason this.status = REJECTED this.onRejectedCallbackArr.forEach(cb => cb()) } } try { executor(resolve, reject) } catch (error) { reject(error) } } then(onResolved, onRejected) { onResolved = typeof onResolved === 'function' ? onResolved : value => value onRejected = typeof onRejected === 'function' ? onRejected : error => { throw error } const promise2 = new Promise((resolve, reject) => { if (this.status === RESOLVED) { process.nextTick(() => { try { const x = onResolved(this.value) resolvePromise(promise2, x, resolve, reject) } catch (error) { reject(error) } }) } if (this.status === REJECTED) { process.nextTick(() => { try { const x = onRejected(this.value) resolvePromise(promise2, x, resolve, reject) } catch (error) { reject(error) } }) } if (this.status === PENDING) { this.onResolvedCallbackArr.push(() => { process.nextTick(() => { try { const x = onResolved(this.value) resolvePromise(promise2, x, resolve, reject) } catch (error) { reject(error) } }) }) this.onRejectedCallbackArr.push(() => { process.nextTick(() => { try { const x = onRejected(this.value) resolvePromise(promise2, x, resolve, reject) } catch (error) { reject(error) } }) }) } }) return promise2 } //==============以下非 Promise/A+ 規範中的內容================== catch(onRejected) { return this.then(null, onRejected) } finally(callback) { return this.then(value => { return Promise.resolve(callback()).then(() => value) }, error => { return Promise.resolve(callback()).then(() => { throw error }) }) } static resolve(value) { if (value instanceof Promise) return value return new Promise((resolve, reject) => { if (((typeof value === 'object' && value !== null) || typeof value === 'function') && typeof value.then === 'function') { process.nextTick(() => { try { value.then(resolve, reject) } catch (error) { reject(error) } }) } else { resolve(value) } }) } static reject(reason) { return new Promise((resolve, reject) => { reject(reason) }) } static all(promises) { return new Promise((resolve, reject) => { if (promises == undefined || !promises[Symbol.iterator]) { const preReason = promises === undefined ? `${promises}` : `${typeof promises} ${promises}` return reject(new TypeError(`${preReason} is not iterable (cannot read property Symbol(Symbol.iterator))`)) } if (promises.length === 0) return resolve([]) let index = 0 const resultArr = [] const processValue = (i, value) => { resultArr[i] = value if (++index === promises.length) { return resolve(resultArr) } } for (let i = 0; i < promises.length; i++) { Promise.resolve(promises[i]).then(value => { processValue(i, value) }, error => { return reject(error) }) } }) } static race(promises) { return new Promise((resolve, reject) => { if (promises == undefined || !promises[Symbol.iterator]) { const preReason = promises === undefined ? `${promises}` : `${typeof promises} ${promises}` return reject(new TypeError(`${preReason} is not iterable (cannot read property Symbol(Symbol.iterator))`)) } if (promises.length === 0) return for (let i = 0; i < promises.length; i++) { Promise.resolve(promises[i]).then(value => { return resolve(value) }, error => { return reject(error) }) } }) } } Promise.defer = Promise.deferred = function () { const dfd = {} dfd.promise = new Promise((resolve, reject) => { dfd.resolve = resolve dfd.reject = reject }) return dfd } module.exports = Promise
promise-comment
/* Promise 實現規範 Promise/A+ 地址: https://promisesaplus.com/ 優點:解決非同步問題 1. 多個併發非同步請求,同時獲取結果 -> promise.all 2. 鏈式非同步請求(惡魔金字塔、回撥地獄)-> promise.then 鏈式呼叫 缺點: 1. 本身還是基於回撥函式的形式 2. 無法中斷非同步處理結果 promise迴圈引用問題: 迴圈引用只能發生在非同步情況下(then 的引數函式中 或 定時器中)。此時建構函式才能執行完畢獲取到當前promise,然後再引用,發生迴圈引用 返回一個新的promise都會執行建構函式(呼叫then的時候) promise遞迴解析問題: 解析promise即 呼叫 該promise的 then方法,將外層promise的resolve reject 作為內層promise返回後 觸發執行的 then方法的 回撥 then的引數函式返回promise會遞迴解析,直到返回非promise或被reject。 底層原理是將then返回的 promise的 resolve和reject 作為引數函式返回的 promise的 then的 回撥, 當引數函式返回的 promise返回後 才觸發執行 建構函式中提供的resolve方法會遞迴解析,直到返回非promise或被reject(reject不會解析promise) 底層原理是將自己和reject作為引數promise的then的回撥,當引數promise返回後,才觸發執行 因此遞迴解析的時候 某內層失敗後,外層依次呼叫其reject方法,也都返回失敗 最內層成功後,外層依次呼叫其resolve方法,也都返回成功 註冊then回撥 遞推的時候不會將then的回撥註冊到微任務佇列尾部 迴歸的時候,promise狀態改變才會註冊到微任務佇列尾部,在下次迴圈執行 promise 異常問題: 1. 如果沒有傳遞executor函式,直接丟擲異常,外面可以同步捕獲 2. 如果在executor函式體中非同步程式碼丟擲異常,外面無法同步捕獲,只能全域性捕獲(或者非同步程式碼自己捕獲,呼叫reject通知外面) 3. 其他情況下promise不會將異常拋到全域性,都是返回一個失敗的promise 4. 如果在executor函式體中同步程式碼丟擲異常 4.1 在resolve或reject之前丟擲的異常,被try-catch捕獲,返回失敗的promise 4.2 在resolve或reject接收的引數函式中丟擲異常,被try-catch捕獲,返回失敗的promise 4.3 在resolve或reject之後丟擲的異常,被try-catch捕獲,不影響promise的狀態 5. 如果在then回撥函式中丟擲異常 5.1 被then中的try-catch捕獲,返回失敗的promise 6. thenable物件 6.1 如果在其then引數函式resolve和reject之前拋異常,都會被try-catch捕獲,返回失敗的promise e.g. Promise.resolve、建構函式中的resolve、then的resolvePromise 6.2 如果在其then引數函式resolve和reject之後拋異常,會被try-catch捕獲,但是不改變promise的狀態 promise then事件迴圈問題: 呼叫then就會將then的引數函式註冊到微任務佇列末尾,在下一輪事件迴圈才會執行(延遲一輪執行) Promise/A+ 測試問題 1. 注掉規範方法中的日誌 2. 注掉非規範中的功能(3個地方) */ const u = require("../utils") const log = u.debugGenerator(__filename) // 狀態(用常量表示) // 1. Promise有三個狀態,resolved(fulfilled) rejected pending(預設初始狀態) // 2. 一旦改變無法修改,只有pending狀態下才可以修改 const RESOLVED = 'RESOLVED' const REJECTED = 'REJECTED' const PENDING = 'PENDING' // 這個方法要相容 所有 其他庫實現的promise,例如 bluebird、q、es6-promise。這些庫可以相互呼叫主要靠 resolvePromise 方法相容 const resolvePromise = (promise2, x, resolve, reject) => { // 迴圈引用-自己等待自己(promise2 和 x 引用同一個物件) if (promise2 === x) { log.debug('promise.then circular reference') // ES6 規範寫法 無法通過Promise/A+測試 return reject('[TypeError: Chaining cycle detected for promise #<Promise>]') // Promise/A+ 規範 // return reject(new TypeError('Chaining cycle detected for promise #<Promise>')) } let called // 標記,防止別的庫實現的promise走成功後又走失敗 // 嚴格根據規範判斷,確保可以相容其他庫實現的promise // if (x instanceof Promise) { } // 不能用這種方式判斷x是不是Promise,因為x可能是別的庫實現的Promise的例項 if ((typeof x === 'object' && x !== null) || typeof x === 'function') { // 如果x是物件或函式 try { // promise(x)都有一個then方法,取x的屬性then,看是不是函式來判斷x是不是promise // 通過 x.then 取值可能會報錯,需要try-catch (參考示例 promise-resolvePromise.js) const then = x.then if (typeof then === 'function') { // 至此,認為x是promise // 不能寫成 x.then,因為這樣會再次取值,有可能報錯 (參考示例 promise-resolvePromise.js) // 用 call 方法,保證then方法中的THIS是需要獲取結果的promise例項(x)。如果不call則是window或global // 如果是thenable物件,then 方法體中可能會報錯,會被catch捕獲到 // 根據內層promise(x)的狀態和值 決定外層promise2的狀態和值 // then.call(x, // y => { // //【這裡呼叫別人實現的promise中的then方法,執行自己傳入的回撥】 // // 無法控制別人的程式碼執行幾個回撥,只能控制自己傳入的回撥(新增判斷)防止走成功後又走失敗 // if (called) return // called = true // // 等 x(promise) 返回成功(值為y)。則執行x的then方法的第一個引數函式(這裡傳入的回撥) // // 即執行當前then方法返回promise2的resolve方法,使當前then返回一個成功的promise2,值為x(promise)的成功結果y // // resolve(y) 但是為了解決返回promise(x)成功又返回promise的現象(y還是一個promise),這裡需要遞迴解析 // log.debug(`before resolvePromise recursion, y is '${y}'`) // // 第一個引數仍然是最外層then返回的promise2(用來保證不發生迴圈引用),resolve、reject 也是promise2的 // // 當y(promise)返回後,呼叫promise2的resolve或reject // // 當最終y不是promise,在【出口1或2】結束,或y返回失敗,迴歸到這裡,巢狀的resolvePromise依次結束 // resolvePromise(promise2, y, resolve, reject) // log.debug(`end resolvePromise recursion, y is '${y}'`) // }, // e => { // // 防止走成功後又走失敗 // if (called) return // called = true // // 同理,如果 x(promise) 返回失敗,則當前then返回的promise2返回失敗,值為x(promise)的失敗原因 // // promise(x)失敗又返回promise(即e是一個promise),不再遞迴解析,直接將最後的promise作為失敗原因返回 // reject(e) // }) // 根據測試結果(4.promise-then.js 最後一個測試用例),需要非同步執行thenable的then方法。使用 process.nextTick(個人理解) // 1. process.nextTick 事件將在當前階段的尾部執行(下次事件迴圈之前) // 2. process.nextTick 將事件維護在 nextTickQueue 中 // 對於promise來說,沒加之前,立即呼叫then將回調放入 nextTickQueue 中;加了之後,先將對then的呼叫放入 nextTickQueue 中 // 執行會後,再將回調放入 nextTickQueue 中。即對於nextTickQueue來說,回撥會延遲執行,但最終都在當前階段執行, // 對事件迴圈整體來說沒有太大的影響 // 3. 無法通過Promise/A+ 測試!!! process.nextTick(() => { then.call(x, y => { //【這裡呼叫別人實現的promise中的then方法,執行自己傳入的回撥】 // 無法控制別人的程式碼執行幾個回撥,只能控制自己傳入的回撥(新增判斷)防止走成功後又走失敗 if (called) return called = true // 等 x(promise) 返回成功(值為y)。則執行x的then方法的第一個引數函式(這裡傳入的回撥) // 即執行當前then方法返回promise2的resolve方法,使當前then返回一個成功的promise2,值為x(promise)的成功結果y // resolve(y) 但是為了解決返回promise(x)成功又返回promise的現象(y還是一個promise),這裡需要遞迴解析 log.debug(`before resolvePromise recursion, y is '${y}'`) // 第一個引數仍然是最外層then返回的promise2(用來保證不發生迴圈引用),resolve、reject 也是promise2的 // 當y(promise)返回後,呼叫promise2的resolve或reject // 當最終y不是promise,在【出口1或2】結束,或y返回失敗,迴歸到這裡,巢狀的resolvePromise依次結束 resolvePromise(promise2, y, resolve, reject) log.debug(`end resolvePromise recursion, y is '${y}'`) }, e => { // 防止走成功後又走失敗 if (called) return called = true // 同理,如果 x(promise) 返回失敗,則當前then返回的promise2返回失敗,值為x(promise)的失敗原因 // promise(x)失敗又返回promise(即e是一個promise),不再遞迴解析,直接將最後的promise作為失敗原因返回 reject(e) }) }) } else { // x 不是 promise(是個普通物件或普通函式),例如:{then:123} // 遞迴出口1【終結者1】 log.debug(`the property 'then' of 'x' is not a function, x is '${x}'`) resolve(x) } } catch (error) { // x.then 取值出錯 // thenable 方法體中,執行了傳入的 resolve 引數函式後,再拋異常,也會進入這裡 log.error(`thenable error '${error}'`) // 防止走成功後又走失敗 if (called) return called = true reject(error) } } else { // 如果x不是物件或函式,直接返回成功狀態的promise2 // 遞迴出口2【終結者2】 log.debug(`x is not a promise, x is ${x}`) resolve(x) } } log.debug('====== my promise ======') class Promise { // 1. 建立類的例項,需要等建構函式中的程式碼全部執行完畢,才能拿到值 // 1.1 如果resolve或reject是非同步呼叫,則建構函式執行完畢返回 PENDING 狀態的promise // 2. THIS // 2.1 建構函式中的THIS指代當前例項物件 constructor(executor) { // executor 執行器 // 1. 建構函式必須傳入一個引數,型別是函式 // 2. 如果不是函式,則直接拋型別錯誤 if (typeof executor !== "function") { throw new TypeError(`Promise resolver ${executor} is not a function`) } this.status = PENDING // 狀態:初始狀態為 pending this.value = undefined // 值: 儲存成功的結果或失敗的原因 this.onResolvedCallbacks = [] // 存放狀態變為成功時的回撥 this.onRejectedCallbacks = [] // 存放狀態變為失敗時的回撥 // 1. 呼叫 resolve 方法 // 1.1 如果value是promise,則非同步(呼叫)執行,作為 promise 成功後then的成功回撥執行 // 1.2 如果value非promise,則同步(呼叫)執行,將value賦值給THIS(如果放到定時器中,屬於非同步呼叫,但是呼叫後是立即同步執行的) // 2. THIS // 2.1 呼叫 resolve 方法的時候沒有指明誰呼叫的,因此這裡的THIS需要明確指向當前例項(使用箭頭函式,THIS是建構函式中的THIS) const resolve = (value) => { // resolve中使用模板字串,無法通過Promise/A+測試 log.debug(`call resolve, status is '${this.status}', value is '${value}'`) // 非同步resolve('this'),會導致迴圈引用-自己等待自己 if (value === this) { // 返回一個失敗的promise return reject(new TypeError('Chaining cycle detected for promise #<Promise>')) } // 這裡不用相容其他版本的promise,只是自己的功能(非規範中的) if (value instanceof Promise) { // 遞迴解析promise,直到value非promise // 是非同步執行(涉及到then) // 呼叫內部then方法,不會丟擲異常 return value.then(resolve, reject) } // resolve解析thenable物件是ES6的功能,無法通過Promise/A+測試 if (((typeof value === 'object' && value !== null) || typeof value === 'function') && typeof value.then === 'function') { // thenable 物件 // 呼叫內部then方法,其回撥是非同步執行的,而呼叫thenable物件中then方法,其回撥是同步的(呼叫thenable.then就會執行) // 因此這裡需要在呼叫的時候非同步(微任務) // 呼叫內部的then方法,無法做手腳。而thenable物件中可以對then方法做手腳,因此這裡要放到try-catch中 return process.nextTick(() => { try { value.then(resolve, reject) } catch (error) { reject(error) } }) } // 只有 pending 狀態可以修改狀態和值(確保resolve和reject只會執行一次) if (this.status === PENDING) { this.value = value this.status = RESOLVED this.onResolvedCallbacks.forEach(cb => cb()) } // JSON.stringify() 丟失註冊的函式 log.debug("------promise is------", this) // 列印resolve所屬的promise } // 1. 呼叫 reject方法,同步執行(如果放到定時器中,屬於非同步呼叫,但是呼叫後是立即同步執行的) // 1.1 不管reason屬於什麼型別值,都原樣賦值給THIS // 1.2 如果reason是promise,reject不會進行解析,直接賦值給THIS const reject = (reason) => { log.debug(`call reject, status is '${this.status}', reason is '${reason}'`) // 只有 pending 狀態可以修改狀態和值(確保resolve和reject只會執行一次) if (this.status === PENDING) { this.value = reason this.status = REJECTED this.onRejectedCallbacks.forEach(cb => cb()) } } // 1. executor函式,立即同步執行 // 1.1 同步程式碼報錯,可以捕獲到異常 // 1.2 非同步程式碼報錯,無法捕獲(當非同步程式碼執行的時候,捕獲異常的函式已經出棧了) // 2. executor 中預設提供 resolve reject 方法 // 2.1 呼叫 resolve 將狀態變為 resolved,值為成功結果。觸發then的成功回撥執行 // 2.2 呼叫 reject 將狀態變為 rejected,值為失敗原因。觸發then的失敗回撥執行 // 2.3 不是靜態方法,不是例項方法,也不是私有方法,就是一個在建構函式中定義的方法 // 2.4 是一個閉包函式,在建構函式中定義,在建立promise的地方執行 // 2.5 呼叫 resolve或reject 不會結束executor函式的執行,即後面的程式碼依然會執行。 // 一般認為,呼叫 resolve或reject後,promise的作用就完成了,後續操作應該放到then方法中, // 因此一般在呼叫 resolve或reject前加上return // 2.6 resolve 和 reject 的引數只能是值型別,如果是個表示式(new建構函式 或 普通函式[呼叫]), // 會先將其在executor函式體中執行,得到表示式的返回值再傳給 resolve 或 reject 執行 // 如果在執行過程中報錯,可以被executor的try-catch捕獲 // 3. 自定義 // 3.1 成功還是失敗(什麼情況下呼叫 resolve/reject)由使用者決定 // 3.2 成功的結果和失敗的原因,由使用者決定 try { executor(resolve, reject) } catch (error) { log.error(`catch executor error: '${error}'`) reject(error) } } // then // 1. Promise 必須具有then方法 // 1.1 then方法需要使用者傳入兩個引數函式(回撥函式),第一個是狀態變為成功時觸發執行(接收成功的結果)【成功回撥】, // 第二個是狀態變為失敗時觸發執行(接收失敗的原因)【失敗回撥】。【兩個引數函式只能觸發執行一個】 // 1.2 如果某個引數函式沒有傳遞,則會使用預設引數函式 // 1.3 then方法同步執行,但是傳入的兩個引數函式(回撥)是非同步執行 // ES6的Promise中then屬於微任務,其他Promise庫可能是巨集任務(bluebird) // 無法自己實現一個微任務,只能呼叫宿主環境提供的API // 1.4 then方法在呼叫引數函式時會傳入'THIS'(呼叫then的promise例項)的值,即引數函式可以拿到當前promise的值 // 2. then方法 返回一個【新】的promise // 2.1 then 方法的執行過程類似執行建構函式,處理完回撥函式(註冊到微任務佇列或新增到待執行佇列)之後,立即返回 PENDING 狀態的promise // 繼續執行後續同步程式碼(因此鏈式呼叫會同步執行then方法,完後再執行then方法的回撥) // 3. then方法 返回promise的狀態 及 鏈式呼叫 promise返回值的傳遞規則: // 3.1 需要在引數函式中用return明確指定返回值,否則then方法預設返回一個成功的promise,值是undefined,傳入下一個then的成功回撥中 // 3.2 如果引數函式返回的是【普通值】(非promise例項、thenable物件、異常,即普通物件、數字、字串、undefined(預設)) // 則then方法返回一個成功的promise,值是該普通值,傳入下一個then的成功回撥中 // 3.3 如果引數函式【丟擲異常】,會被then內部的try-catch捕獲 // 則then方法返回一個失敗的promise,值是異常原因,傳入下一個then的失敗回撥中 // 3.4 如果引數函式返回一個【promise例項】,則該promise例項的狀態會決定當前then方法返回promise的狀態,從而決定下一個then引數函式的執行情況 // 3.4.1 如果引數函式返回一個成功的promise,則當前then也返回一個成功的promise,值是引數函式返回promise的成功結果,傳入下一個then的成功回撥中 // 3.4.2 如果引數函式返回一個失敗的promise,則當前then也返回一個失敗的promise,值是引數函式返回promise的失敗原因,傳入下一個then的失敗回撥中 // 4. 錯誤處理 // 4.1 如果距離自己最近的then沒有傳遞第二個引數函式,則找下一個then或catch // 5. THIS // 5.1 then方法中的THIS是呼叫then的promise例項 then(onResolved, onRejected) { // 方法中的THIS是呼叫then的promise例項 log.info(`call then, promise status is ${this.status}`) // 判斷是否傳遞引數以及傳遞的是不是函式 // onResolved = typeof onResolved === 'function' ? onResolved : value => { return value } onResolved = typeof onResolved === 'function' ? onResolved : v => v // onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err } onRejected = typeof onRejected === 'function' ? onRejected : e => { throw e } // 懶遞迴,每次呼叫就new一個新的promise const promise2 = new Promise((resolve, reject) => { // then方法同步執行(判斷是同步的),回撥函式非同步執行 if (this.status === RESOLVED) { // then中回撥函式非同步執行,可以用 setTimeout 或 process.nextTick 模擬實現【只能用一種,不能混用】 // ES6 規範中 then 是微任務,這裡無法自己實現一個微任務,只能呼叫宿主環境提供的API(process.nextTick) // then方法同步執行到這裡,建立匿名函式的時候,promise2 還沒有定義(等建構函式中的程式碼全部執行完畢,才能拿到promise2) // 建構函式還沒有執行完,但是在建構函式中就使用了例項,因此匿名函式的執行一定是非同步的,才能在執行時拿到例項 // setTimeout(() => { // try { // const x = onResolved(this.value) // log.debug("RESOLVED:then return x =", x) // resolvePromise(promise2, x, resolve, reject) // } catch (error) { // reject(error) // } // }, 0) process.nextTick(() => { try { // 回撥函式非同步執行,外面executor的try-catch無法捕獲到異常,因此需要在源頭捕獲 const x = onResolved(this.value) // 如果x是普通值,可以直接 resolve(x) log.debug("RESOLVED:then return x =", x) // 遞迴解析回撥函式的返回值x,決定then返回的promise2的狀態 // 如果x是promise,呼叫該promise的then方法時,傳遞的兩個引數函式就是當前then返回promise2的executor中提供的resolve reject // 1. 如果該promise返回成功,則呼叫當前then返回promise2的resolve方法,使當前then返回一個成功的promise2 // 2. 如果該promise返回失敗,則呼叫當前then返回promise2的reject方法,使當前then返回一個失敗的promise2 resolvePromise(promise2, x, resolve, reject) } catch (error) { // 引數函式異常,then返回一個失敗的promise2 log.error("RESOLVED: catch error:", error) reject(error) } }) } if (this.status === REJECTED) { // setTimeout(() => { // try { // const x = onRejected(this.value) // log.debug("REJECTED:then return x =", x) // resolvePromise(promise2, x, resolve, reject) // } catch (error) { // reject(error) // } // }, 0) process.nextTick(() => { try { const x = onRejected(this.value) // 如果x是普通值,可以直接 resolve(x) log.debug("REJECTED:then return x =", x) resolvePromise(promise2, x, resolve, reject) } catch (error) { log.error("REJECTED: catch error:", error) reject(error) } }) } // 如果 executor 裡面非同步呼叫resolve或reject,則呼叫then方法時,當前promise是pending狀態 // 如果當前狀態是 pending,需要用釋出訂閱模式,則將傳入的回撥函式儲存起來,稍後執行resolve或reject改變狀態時再觸發執行 // 同一個promise可以多次呼叫 then 方法,因此會有多個回撥函式,需要用陣列儲存 if (this.status === PENDING) { // AOP this.onResolvedCallbacks.push(() => { // 不是立即執行,當執行外面的匿名函式的時候,才會執行 // do other things... // setTimeout(() => { // try { // const x = onResolved(this.value) // log.debug("PENDING->RESOLVED:then return x =", x) // resolvePromise(promise2, x, resolve, reject) // } catch (error) { // reject(error) // } // }, 0) process.nextTick(() => { try { const x = onResolved(this.value) // 如果x是普通值,可以直接 resolve(x) log.debug("PENDING->RESOLVED:then return x =", x) resolvePromise(promise2, x, resolve, reject) } catch (error) { log.error("PENDING->RESOLVED: catch error:", error) reject(error) } }) }) this.onRejectedCallbacks.push(() => { // setTimeout(() => { // try { // const x = onRejected(this.value) // log.debug("PENDING->REJECTED:then return x =", x) // resolvePromise(promise2, x, resolve, reject) // } catch (error) { // reject(error) // } // }, 0) process.nextTick(() => { try { const x = onRejected(this.value) // 如果x是普通值,可以直接 resolve(x) log.debug("PENDING->REJECTED:then return x =", x) resolvePromise(promise2, x, resolve, reject) } catch (error) { log.error("PENDING->REJECTED: catch error:", error) reject(error) } }) }) } }) return promise2 } //=============================================以下非Promise/A+ 規範=============================================== // 返回一個新的promise,根據 onRejected 的返回結果決定返回promise的狀態 catch(onRejected) { // THIS指代呼叫catch的promise例項 return this.then(null, onRejected) } // node>10 // 表示前面的promise無論成功還是失敗都會執行finally方法 // 當無論如何必須要處理一個邏輯的時候使用,如果返回成功的promise不影響整個then鏈的結果 // callback // 1. 呼叫callback不會傳遞引數(無法拿到前面promise的返回值) // 2. callback最終在then的引數函式中被呼叫 // 3. callback返回一個promise(如果不是則用Promise.resolve轉換為promise),且會等待這個promise返回 // finally值傳遞規則 // 呼叫then方法返回一個promise,根據callback的執行結果決定自己的狀態和值 // 1. 如果callback返回的promise成功,則finally返回成功的promise,值為前面promise的成功結果,傳遞下去(遵循 then 的鏈式呼叫原理) // 2. 如果callback返回的promise失敗,則finally返回失敗的promise,值為callback返回promise的失敗原因,取代並傳遞下去(遵循 then 的鏈式呼叫原理) // 3. 如果callback執行報錯,則被當前then回撥的try-catch捕獲,finally返回失敗的promise,值為報錯原因,取代並傳遞下去 finally(callback) { log.info(`call finally, promise is ${JSON.stringify(this)}`) return this.then(value => { log.debug("finally: previous promise is resolved") // 如果前面promise成功,則進入這裡 // 執行順序:在回撥函式中: // 1.執行 callback(),返回一個值 // 2.執行 Promise.resolve(),返回一個promise // 3.執行 then方法,處理回撥 '()=>value' // 4.返回一個 PENDING 狀態的promise。(此時對外面的then方法來說就是第一個引數回撥返回值x是一個promise,繼續解析) return Promise.resolve(callback()).then(() => value) }, err => { log.debug("finally: previous promise is rejected") // 如果前面的promise失敗,則進入這裡 return Promise.resolve(callback()).then(() => { throw err }) }) } // 將當前值轉換為promise物件:Promise.resolve([value]) // 引數: // 1. 是一個promise例項,則直接原樣返回 // 2. 是一個thenable物件,則非同步呼叫其then方法,決定resolve返回promise的狀態 // 2.1 Promise.resolve([thenable]) 可能會返回一個失敗的promise // 3. 不是thenable物件或promise例項,則返回一個新的成功的promise,值為該引數 // 4. 不傳引數,返回一個新的成功的promise,值為undefined static resolve(value) { // 不處理相容 if (value instanceof Promise) { // 原樣返回 return value } return new Promise((resolve, reject) => { if (((typeof value === 'object' && value !== null) || typeof value === 'function') && typeof value.then === 'function') { // thenable 物件 // 呼叫內部then方法,其回撥是非同步執行的,而呼叫thenable物件中then方法,其回撥是同步的(呼叫thenable.then就會執行) // 因此這裡需要在呼叫的時候非同步(微任務) // 呼叫內部的then方法,無法做手腳。而thenable物件中可以對then方法做手腳,因此這裡要放到try-catch中 process.nextTick(() => { try { value.then(resolve, reject) } catch (error) { reject(error) } }) } else { return resolve(value) } }) } // 將當前值轉換為一個失敗的promise物件:Promise.reject([value]) static reject(reason) { return new Promise((resolve, reject) => { reject(reason) }) } // 引數:實現iterator介面的可迭代物件(陣列、字串) // 1. 如果引數不存在或者不可迭代,返回一個失敗的promise,值為型別錯誤 // 2. 如果可迭代物件成員為空,返回一個成功的promise,值為空陣列 // 3. 如果可迭代物件成員不是promise,則呼叫 Promise.resolve 將其變為一個promise // 返回promise的狀態:由所有可迭代物件的成員(promise)的返回狀態決定 // 1. 所有成員promise都返回成功,則all返回一個成功的promise,值為所有成員promise返回值組成的陣列(按成員順序排序) // 2. 只要一個成員promise返回失敗,則all返回一個失敗的promise,值為第一個失敗的成員promise的失敗原因 // 3. 如果成員promise自身定義了catch方法,那麼它被rejected時會被自身定義的catch捕獲, // 並返回一個新的promise(用這個新promise狀態代替該成員promise狀態) static all(promises) { return new Promise((resolve, reject) => { if (promises == undefined || !promises[Symbol.iterator]) { const preReason = promises === undefined ? `${promises}` : `${typeof promises} ${promises}` return reject(new TypeError(`${preReason} is not iterable (cannot read property Symbol(Symbol.iterator))`)) } if (promises.length === 0) return resolve([]) let index = 0 const resultArr = [] function processValue(i, value) { resultArr[i] = value if (++index === promises.length) { resolve(resultArr) } } for (let i = 0; i < promises.length; i++) { //promises[i] 可能是普通值,用 Promise.resolve 包一層,確保都是promise Promise.resolve(promises[i]).then((value) => { processValue(i, value) }, (err) => { // 有一個失敗則結束迴圈 return reject(err) }) } }) } // 引數:實現iterator介面的可迭代物件(陣列、字串) // 1. 如果引數不存在或者不可迭代,返回一個失敗的promise,值為型別錯誤 // 2. 如果可迭代物件成員為空,【返回一個PENDING 狀態的promise】 // 3. 如果可迭代物件成員不是promise,則呼叫 Promise.resolve 將其變為一個promise // 返回promise的狀態: // 1. 只要一個成員promise返回,則race返回相同狀態的promise static race(promises) { return new Promise((resolve, reject) => { if (promises == undefined || !promises[Symbol.iterator]) { const preReason = promises === undefined ? `${promises}` : `${typeof promises} ${promises}` return reject(new TypeError(`${preReason} is not iterable (cannot read property Symbol(Symbol.iterator))`)) } if (promises.length === 0) return for (let i = 0; i < promises.length; i++) { Promise.resolve(promises[i]).then((value) => { return resolve(value) }, (err) => { return reject(err) }); } }); } } // 測試入口 // Promise的延遲物件,測試的時候會呼叫這個函式 Promise.defer = Promise.deferred = function () { const dfd = {} dfd.promise = new Promise((resolve, reject) => { dfd.resolve = resolve dfd.reject = reject }) return dfd } module.exports = Promise
測試
更詳細的測試用例參考repo https://github.com/lfp1024/promise
resolve 解析 promise
- resolve(promise) 可以遞迴解析,由內層promise的狀態決定外層promise的狀態
- reject(promise) 不會遞迴解析,外層promise直接返回失敗,失敗原因是promise
var p1 = new Promise(function (resolve, reject) {
resolve(new Promise(res => res('resolve')));
});
var p2 = new Promise(function (resolve, reject) {
// 將resolve實參promise的狀態變為自己的狀態,將實參promise的值變為自己的值
resolve(new Promise((res, rej) => { rej('reject') }));
});
var p3 = new Promise(function (resolve, reject) {
reject(new Promise(res => res('resolve')));
});
p1.then(
function fulfilled(value) { console.log('p1 fulfilled: ' + value); },
function rejected(err) { console.log('p1 rejected: ' + err); }
);
p2.then(
function fulfilled(value) { console.log('p2 fulfilled: ' + value); },
function rejected(err) { console.log('p2 rejected: ' + err); }
);
p3.then(
function fulfilled(value) { console.log('p3 fulfilled: ' + value); },
function rejected(err) { console.log('p3 rejected: ' + err); }
);
// 輸出結果:
// p3 rejected: [object Object]
// p1 fulfilled: resolve
// p2 rejected: reject
/*
解釋:
// 同步程式碼執行
p1 的 resolve 註冊到下一輪迴圈執行
p2 的 resolve 註冊到下一輪迴圈執行
p3 的 reject 當前執行棧中同步執行,將 p3.then 註冊到下一輪迴圈執行
// 下一輪迴圈
p1 的 resolve 執行,將 p1.then 註冊到下一輪迴圈執行
p2 的 resolve 執行,將 p2.then 註冊到下一輪迴圈執行
p3.then 執行,輸出 => p3 rejected: [object Object]
// 下一輪迴圈
p1.then 執行,輸出 => p1 fulfilled: resolve
p2.then 執行,輸出 => p2 rejected: reject
*/
resolve 解析 thenable物件
- ES6 的resolve可以解析thenable物件。且thenable.then屬於微任務
- thenable.then方法中的引數函式的執行情況會決定當前promise的狀態
console.log("--script start--")
let obj = {
then: function (onResolved, onRejected) {
console.log("非同步執行thenable then")
onResolved('成功啦')
}
}
setTimeout(() => {
console.log("setTimeout")
}, 0);
let p1 = new Promise(function (resolve, reject) {
console.log("1")
resolve(obj);
console.log("2")
});
p1.then(res=>{
console.log("promise then")
})
console.log("p1 =", p1)
setTimeout(() => {
console.log("p1 = ", p1)
}, 0);
console.log("--script end--")
// 從輸出順序可以看出呼叫thenable的then方法屬於微任務
// --script start--
// 1
// 2
// p1 = Promise { <pending> }
// --script end--
// 非同步執行thenable then
// promise then
// setTimeout
// p1 = Promise { '成功啦' }
// 如果是巨集任務,則輸出順序應為(呼叫resolve之前,p1.then尚未註冊到事件佇列中)
// --script start--
// 1
// 2
// p1 = Promise { <pending> }
// --script end--
// setTimeout
// 非同步執行thenable then
// promise then
// p1 = Promise { '成功啦' }
then 遞迴解析 promise
- then 會遞迴解析其回撥函式返回的promise。最內層promise的狀態決定外層then返回promise的狀態
let p = new Promise((resolve, reject) => {
resolve(1)
})
// [resolve(promise)]-------------------------------------------------
// 3層promise:最外層p1,中間層p2(then的第一個回撥返回的promise),最內層p3(1秒後成功或失敗)
let p1 = p.then(value => {
// then的引數函式返回一個 new Promise
return new Promise((resolve, reject) => {
setTimeout(() => {
// resolve 又返回一個promise,遞迴解析
resolve(new Promise((resolve, reject) => {
setTimeout(() => {
// 最內層promise呼叫resolve成功之後,會執行其then的第一個回撥,即執行中間層p2的resolve
// resolve("success")
// 最內層promise呼叫reject失敗之後,會執行其then的第二個回撥,即執行中間層p2的reject
reject("fail")
}, 1000);
}))
}, 1000);
})
})
// 外面想要獲取當前then返回promise的結果,還是要呼叫當前then返回promise的then方法
p1.then(res => {
console.log("當前then返回promise的結果1", res) // 當前then返回promise的結果1 success
}, err => {
console.log("當前then返回promise的原因1", err) // 當前then返回promise的原因1 fail
})
// [reject(promise)]-------------------------------------------------
let promise = new Promise((resolve, reject) => {
resolve(1)
})
let promise2 = promise.then(value => {
// then的引數函式返回一個 new Promise
return new Promise((resolve, reject) => {
setTimeout(() => {
// reject 又返回的一個promise,不再遞迴解析(但是會執行new表示式,執行完後立即返回一個 PENDING 狀態的promise)
reject(new Promise((resolve, reject) => {
console.log("執行")
setTimeout(() => {
resolve("success")
}, 1000);
}))
}, 1000);
})
})
// 外面想要獲取當前then返回promise的結果,還是要呼叫當前then返回promise的then方法
promise2.then(res => {
console.log("當前then返回promise的結果2", res)
}, err => {
console.log("當前then返回promise的原因2", err) // 當前then返回promise的原因2 Promise { <pending> }
})
then 解析 thenable 物件
- then解析 thenable 非同步呼叫其then 方法
console.log("--script start--")
let obj = {
then: function (onResolved, onRejected) {
console.log("非同步執行thenable then")
onResolved('成功啦')
// onRejected('失敗啦')
// throw Error("Oops!")
}
}
setTimeout(() => {
console.log("setTimeout")
}, 0)
let p = Promise.resolve('ok')
let p1 = p.then(res => {
console.log("p2 then")
return obj
})
let p2 = Promise.resolve('ok2')
let p3 = p2.then(res => {
console.log("p3 then")
return
})
console.log("p1 =", p1)
setTimeout(() => {
console.log("p1 = ", p1)
}, 0)
console.log("--script end--")
// 輸出:
// --script start--
// p1 = Promise { <pending> }
// --script end--
// p2 then
// p3 then
// 非同步執行thenable then
// setTimeout
// p1 = Promise { '成功啦' }
// 如果不是非同步呼叫,順序是:
// --script start--
// p1 = Promise { <pending> }
// --script end--
// p2 then
// 非同步執行thenable then
// p3 then
// setTimeout
// p1 = Promise { '成功啦' }
then 巨集任務實現和微任務實現
let fs = require('fs')
// 需求是讀取一個檔案獲取路徑,然後再繼續讀取內容
// 當前路徑下有兩個檔案 name.txt 和 age.txt。name.txt 內容是 age.txt。age.txt 內容是 27。
// 先讀取 name.txt 拿到 age.txt,再讀取 age.txt 內容
fs.readFile('./name.txt', 'utf-8', (err, data) => {
if (err) {
// 錯誤處理...
}
console.log("data = ", data)
fs.readFile(data, 'utf-8', (err, data) => {
if (err) {
// 錯誤處理...
}
console.log("data = ", data)
})
})
function readFileSync(filePath) {
return new Promise((res, rej) => {
fs.readFile(filePath, 'utf-8', (err, data) => {
if (err) return rej(err)
return res(data)
})
})
}
readFileSync('./name.txt').then((data) => {
console.log('獲取到資料', data)
return readFileSync(data)
}).then((data) => {
console.log('獲取到資料', data)
}, (err) => {
console.log('失敗', err)
})
/*
用 setTimeout 實現then的非同步呼叫(巨集任務),跟es6執行順序不同
輸出結果:
data = age.txt
data = 27
獲取到資料 age.txt
獲取到資料 27
分析:
// 第一輪
fs.readFile 新增一個巨集任務1(讀取到資料之後觸發)
readFileSync 新增一個巨集任務2(讀取到資料之後觸發)
// 第二輪(讀取到資料)
巨集任務1執行,輸出=>data = age.txt,新增巨集任務3
巨集任務2執行,新增巨集任務4(then)
// 第三輪
巨集任務3執行,輸出=>data = 27
// 第四輪
巨集任務4執行,輸出=>獲取到資料 age.txt,新增巨集任務5
// 第五輪
巨集任務5執行,新增巨集任務6(then)
// 第六輪
巨集任務6執行,輸出=>獲取到資料 27
*/
/*
換用 process.nextTick 來實現則執行順序相同
輸出結果:
data = age.txt
獲取到資料 age.txt
data = 27
獲取到資料 27
分析:
// 第一輪
fs.readFile 新增一個巨集任務1(讀取到資料之後觸發)
readFileSync 新增一個巨集任務2(讀取到資料之後觸發)
// 第二輪(讀取到資料)
巨集任務1執行,輸出=>data = age.txt,新增巨集任務3
巨集任務2執行,新增微任務1(then)
// 第三輪
微任務1執行,輸出=>獲取到資料 age.txt,新增巨集任務4
// 第四輪
巨集任務3執行,輸出=>data = 27
// 第五輪
巨集任務4執行,新增微任務2(then)
// 第六輪
微任務2執行,輸出=>獲取到資料 27
*/
Promise.resolve 解析 promise
- Promise.resolve 解析promise
- Promise.reject 不解析promise
var p1 = new Promise(function (resolve, reject) {
resolve(Promise.resolve('resolve'));
});
var p2 = new Promise(function (resolve, reject) {
resolve(Promise.reject('reject'));
});
var p3 = new Promise(function (resolve, reject) {
reject(Promise.resolve('resolve'));
});
p1.then(
function fulfilled(value) { console.log('p1 fulfilled: ' + value); },
function rejected(err) { console.log('p1 rejected: ' + err); }
);
p2.then(
function fulfilled(value) { console.log('p2 fulfilled: ' + value); },
function rejected(err) { console.log('p2 rejected: ' + err); }
);
p3.then(
function fulfilled(value) { console.log('p3 fulfilled: ' + value); },
function rejected(err) { console.log('p3 rejected: ' + err); }
);
// p3 rejected: [object Promise]
// p1 fulfilled: resolve
// p2 rejected: reject