promise 實現 async await 源碼及原理分析

async-await

源碼 https://github.com/lfp1024/promise
Promise 參考上一篇博文 https://www.cnblogs.com/usmile/p/13347651.html

async-await

const _async = (func) => {
    const p = new Promise((resolve, reject) => {
        try {
            const value = func()
            if (((typeof value === 'object' && value !== null) || typeof value === 'function') &&
                typeof value.then === 'function') {
                Promise.resolve(value).then(resolve, reject)
            } else {
                resolve(value)
            }
        } catch (error) {
            reject(error)
        }
    })
    return p
}

const _await = (() => {
    return (arg) => {
        return (onResolved, onRejected) => {
            const innerPromise = onRejected ? Promise.resolve(arg).catch(onRejected).then(onResolved, onRejected)
                : Promise.resolve(arg).then(onResolved, onRejected)
            return innerPromise
        }
    }
})()


module.exports = {
    _async,
    _await
}

async-await-comment

/* 
async await 是promise的語法糖,優化promise的then鏈寫法,用同步的方式編寫異步代碼

async 異步函數(包含函數聲明、函數表達式、Lambda表達式[箭頭函數]等使用形式)
1. 返回一個 Promise 對象
  1. 直接返回成功或失敗狀態的promise
    1.1 函數體沒有await,return 一個普通值(非promise和thenable對象,默認undefined),async立刻返回一個成功狀態的promise,值爲該普通值
    1.2 函數體中沒有await或在await之前,拋出異常,async立即返回失敗的promise,值爲失敗原因,異常不會拋到函數體外面影響外面代碼的執行
  2. 先返回PENDING狀態的promise,然後再異步修改狀態
    2.1 函數體中有await,在await獲取到值之前,async先返回 PENDING 狀態的promise,然後再根據await後面表達式返回promise的狀態而改變
    2.2 如果await後面表達式返回的promise失敗且未捕獲異常,則async返回的promise失敗,失敗原因是表達式返回promise的失敗原因
2. 最外層async無法用 await 獲取其返回值,應該用原來的方式:then() 鏈來處理async返回的 promise 對象

await 表達式(包含promise對象,普通函數調用、基本值類型)
1. 【等待】表達式的【返回值】
    1.1 如果表達式的值是promise對象,則等待promise返回(調用其then方法,異步獲取),並將其返回值作爲await表達式的值
    1.2 如果表達式的值不是promise對象,則通過 Promise.resolve 轉換爲 promise對象,等待其返回,並將其返回值作爲await表達式的值
2. await相當於調用後面表達式返回promise的then方法,異步(等待)獲取其返回值。即 await<==>promise.then
    2.1 不管代碼中是否用到await表達式返回值,await都會去獲取(調用其then方法),在獲取到之前,async會返回一個 PENDING 狀態的promise。
    2.2 函數體中await表達式後面的代碼相當於promise.then方法的第一個回調(onResolved),可以拿到表達式返回promise的返回值(即await表達式返回值)
        因此await會阻塞函數體中後面代碼的執行(異步執行then的回調),但是表達式是同步執行的【因此await操作符只能出現在async異步函數中】
        如果await表達式後面沒有代碼,則相當於then的第一個回調不傳,使用默認回調函數(v=>v)
    2.3 調用promise.then方法的第二個回調默認不傳,使用默認回調函數(err=>{throw err})
        因此當表達式報錯或返回失敗的promise,await會將該異常拋出到函數體中,可以(需要)通過try-catch捕獲異常
        如果await promise調用了其catch方法,則不會拋出,因爲catch也返回一個promise,相當於await調用catch返回promise的then方法
        第二個回調傳遞方式:
          1. 當表達式返回值是promise且調用其catch方法時,相當於傳遞了第二個回調(即catch方法中的回調)
          2. 當await表達式放在try-catch中時,相當於傳遞了第二個回調(即catch方法中的回調)
*/

//===================自己實現async、await=====================
const u = require("../utils")
const log = u.debugGenerator(__filename)

/** 
 *@param func: 異步函數 
 */ 
const _async = (func) => {
    const p = new Promise((resolve, reject) => {
        try {
            const value = func()
            if (((typeof value === 'object' && value !== null) || typeof value === 'function') &&
                typeof value.then === 'function') {
                log.debug("===value is a thenable obj===")
                // promise 或 thenable

                // 1. 如果返回一個thenable對象,這裏需要用Promise.resolve轉爲promise,以達到異步調用thenable.then的效果
                // 2. 如果返回一個promise,Promise.resolve原樣返回,無影響。因此統一用Promise.resolve轉爲promise
                //    2.1 如果函數體有await,則這裏相當於_await返回的 innerPromise.then(resolve,reject)
                Promise.resolve(value).then(resolve, reject)
                setTimeout(() => {
                    log.info("異步 async  的 p =", p)
                }, 0);
            } else {
                // 普通值(undefined、123、"123"、{then:123}) 立即返回成功的promise
                resolve(value)
            }
            log.debug("========async return==========")
        } catch (error) {
            log.debug("===value is not a thenable obj===")
            //  3. 如果函數體中同步代碼報錯,則返回失敗的promise,值爲失敗原因
            log.error("========async catch===========\n", error)
            reject(error)
        }
    })
    log.info("同步 async  的 p =", p)
    return p
}


/**
 * @param arg: await後面的表達式
 * @param onResolved: 函數體中await表達式下面的代碼
 * @param onRejected: 函數體中的catch回調函數
 */
// 注意變形之後需要加 return _await ...
// 多個await,變形後會嵌套調用_await,這裏用計數器n區分
// await promise自帶catch或被try-catch包裹,相當於將catch的回調函數作爲 onRejected 傳入
const _await = (() => {
    let n = 0
    return (arg) => {
        n++
        return (onResolved, onRejected) => {
            // Promise.resolve(arg) 返回失敗,執行 onRejected (如果沒有傳遞則執行then的默認失敗回調,innerPromise失敗)
            // Promise.resolve(arg) 返回成功,執行 onResolved
            // onResolved 的執行結果決定then返回innerPromise的狀態,從而決定async返回promise的狀態
            // onResolved 拋異常,then內部會捕獲,返回innerPromise失敗,async返回promise失敗
            let innerPromise = onRejected ? Promise.resolve(arg).catch(onRejected).then(onResolved, onRejected)
                : Promise.resolve(arg).then(onResolved, onRejected)
            setTimeout(((n) => {
                return () => {
                    log.info('異步 then-' + n + ' 的 p =', innerPromise)
                }
            })(n), 0);
            log.info('同步 then-' + n + ' 的 p =', innerPromise)
            return innerPromise
        }
    }
})()

module.exports = {
    _async,
    _await
}



/*
// 傳統promise和async-await編輯器自動轉換

//catch方法轉換爲try-catch
function f() {
    return new Promise((res, rej) => {
        setTimeout(() => {
            rej('err')
        }, 1000);
    }).then(data => {
        console.log(data)
    }).catch(err => {
        console.log(err)
    })
}

async function f() {
    try {
        const data = await new Promise((res, rej) => {
            setTimeout(() => {
                rej('err');
            }, 1000);
        });
        console.log(data);
    }
    catch (err) {
        console.log(err);
    }
}

//then的第二個回調和catch方法都轉換爲try-catch

function f() {
    return new Promise((res, rej) => {
        setTimeout(() => {
            rej('err')
        }, 1000);
    }).then(data => {
        console.log(data)
    }, e => {
        console.log(e)
    }).catch(err => {
        console.log(err)
    })
}

async function f() {
    try {
        try {
            const data = await new Promise((res, rej) => {
                setTimeout(() => {
                    rej('err')
                }, 1000)
            })
            console.log(data)
        }
        catch (e) {
            console.log(e)
        }
    }
    catch (err) {
        console.log(err)
    }
}

//多個await

function f() {
    return new Promise((res, rej) => {
        setTimeout(() => {
            rej('err')
        }, 1000);
    }).then(data => {
        return new Promise((res, rej) => {
            res("suc")
        }).then(data => {
            console.log(data)
        })
    }).catch(err => {
        console.log(err)
    })
}

async function f() {
    try {
        const data = await new Promise((res, rej) => {
            setTimeout(() => {
                rej('err')
            }, 1000)
        })
        const data_1 = await new Promise((res, rej) => {
            res("suc")
        })
        console.log(data_1)
    }
    catch (err) {
        console.log(err)
    }
}

*/

測試

await promise.catch

  • await 後面promise自帶catch方法,則失敗或拋異常會被自己的catch捕獲,不影響async函數體中後面代碼的執行
async function f() {
    console.log("1")
    // await 異步獲取返回值
    const r = await new Promise((res, rej) => {
        console.log("2")
        rej("1 error")
        console.log("3")
    }).catch(err => {
        console.log('i catch you', err)
        return 123  // catch 捕獲異常,await不拋出,await表達式的值由catch的返回值決定
    })
    await new Promise((res, rej) => {
        console.log("4",r)
        rej("2 error")
    })
    console.log("res = ", r)
}

console.log("a")
let p = f()
console.log(p)
console.log("b")
setTimeout(() => {
    console.log(p)
}, 0);

// 輸出
// a
// 1
// 2
// 3
// Promise { <pending> }
// b
// i catch you 1 error
// 4 123
// Promise { <rejected> '2 error' }

// 變形
console.log("a")
let p = _async(function f() {
    console.log("1")
    return _await(new Promise((res, rej) => {
        console.log("2")
        rej("1 error")
        console.log("3")
    }))((r) => {
        return _await(new Promise((res, rej) => {
            console.log("4", r)
            rej("2 error")
        }))(() => {
            console.log("res = ", r)
        })
        // catch 回調作爲 onRejected 傳入
    }, (err) => {
        console.log('i catch you', err)
        return 123  
    })
})
console.log(p)
console.log("b")
setTimeout(() => {
    console.log(p)
}, 0);

轉變爲自己實現的 _async 和 _await 示意圖

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章