1. 程式人生 > 實用技巧 >Luogu3402 可持久化並查集

Luogu3402 可持久化並查集

Promise

原始碼 https://github.com/lfp1024/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