promise 實現 async await 原始碼及原理分析
阿新 • • 發佈:2020-07-21
async-await
原始碼 https://github.com/lfp1024/promise
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 示意圖