V8 9.1 正式支持頂層 await !



作者:ConardLi
從 v9.1 開(kāi)始,在 V8 中默認(rèn)啟用頂級(jí) await,并且在沒(méi)有 --harmony-top-level-await 配置的情況下也是可以用的。
在
Blink渲染引擎中,v89版本默認(rèn)情況下已經(jīng)啟用了頂層await
什么是頂層 await
在以前,我們必須在一個(gè) async 函數(shù)中才能使用 await,如果直接在一個(gè)模塊最外層使用 await 會(huì)拋出 SyntaxError 異常,為此我們通常會(huì)在外面包裹一個(gè)立即執(zhí)行函數(shù):
await Promise.resolve(console.log('??'));
// → SyntaxError: await is only valid in async function
(async function() {
await Promise.resolve(console.log('??'));
// → ??
}());
現(xiàn)在我們可以在整個(gè)模塊的最外層直接使用 await,這讓我們的整個(gè)模塊看一來(lái)就像一個(gè)巨大的 async 函數(shù)。
await Promise.resolve(console.log('??'));
// → ??
注意,頂層
await僅僅是允許我們?cè)谀K的最外層允許使用await,傳統(tǒng)的script標(biāo)簽或非async函數(shù)均不能直接使用。
為什么要引入頂層 await
下面舉一個(gè)我們實(shí)際開(kāi)發(fā)中可能會(huì)遇到的一個(gè)問(wèn)題:
工具庫(kù)模塊
在一個(gè)工具庫(kù)模塊中,我們導(dǎo)出了兩個(gè)函數(shù):
//------ library.js ------
export const sqrt = Math.sqrt;
export function square(x) {
return x * x;
}
export function diagonal(x, y) {
return sqrt(square(x) + square(y));
}
中間件
在一個(gè)中間件中,我們每次需要等待一些事情執(zhí)行完,再執(zhí)行工具庫(kù)導(dǎo)出的兩個(gè)函數(shù):
//------ middleware.js ------
import { square, diagonal } from './library.js';
console.log('From Middleware');
let squareOutput;
let diagonalOutput;
// IIFE
(async () => {
await delay(1000);
squareOutput = square(13);
diagonalOutput = diagonal(12, 5);
})();
function delay(delayInms) {
return new Promise(resolve => {
setTimeout(() => {
resolve(console.log('??'));
}, delayInms);
});
}
export {squareOutput,diagonalOutput};
主程序
在主程序中,我們要調(diào)用中間件導(dǎo)出的兩個(gè)值,但是我們并不能直接立刻拿到結(jié)果,而是必須自己寫(xiě)一個(gè)異步等待的代碼才能拿到:
//------ main.js ------
import { squareOutput, diagonalOutput } from './middleware.js';
console.log(squareOutput); // undefined
console.log(diagonalOutput); // undefined
console.log('From Main');
setTimeout(() => console.log(squareOutput), 2000);
//169
setTimeout(() => console.log(diagonalOutput), 2000);
//13
解決方案
這時(shí),我們可能就會(huì)用到我們的主角,頂層 await:
//------ middleware.js ------
import { square, diagonal } from './library.js';
console.log('From Middleware');
let squareOutput;
let diagonalOutput;
//使用頂層 await
await delay(1000);
squareOutput = square(13);
diagonalOutput = diagonal(12, 5);
function delay(delayInms) {
return new Promise(resolve => {
setTimeout(() => {
resolve(console.log('??'));
}, delayInms);
});
}
export {squareOutput,diagonalOutput};
//------ main.js ------
import { squareOutput, diagonalOutput } from './middleware.js';
console.log(squareOutput); // 169
console.log(diagonalOutput); // 13
console.log('From Main');
setTimeout(() => console.log(squareOutput), 2000);// 169
setTimeout(() => console.log(diagonalOutput), 2000); // 13
在以上的代碼中, main.js 會(huì)等待 middleware.js 中的 await promise 被 resolve 后,才會(huì)執(zhí)行它的代碼,是不是非常方便!
其他應(yīng)用場(chǎng)景
動(dòng)態(tài)依賴導(dǎo)入
這允許在模塊的運(yùn)行時(shí)環(huán)境中確認(rèn)依賴項(xiàng),在開(kāi)發(fā)/生產(chǎn)環(huán)境切換、國(guó)際化等場(chǎng)景中非常有用。
const strings = await import(`/i18n/${navigator.language}`);
資源初始化
const connection = await dbConnector();
這允許模塊申請(qǐng)資源,同時(shí)也可以在模塊不能使用時(shí)拋出錯(cuò)誤。
依賴回退
下面的例子首先會(huì)從 CDN A 加載 JavaScript 庫(kù),如果它加載失敗會(huì)將 CDN B 作為備份加載。
let jQuery;
try {
jQuery = await import('https://cdn-a.example.com/jQuery');
} catch {
jQuery = await import('https://cdn-b.example.com/jQuery');
}
模塊的執(zhí)行順序
JavaScript 中一個(gè)使用 await 的巨大改變是模塊樹(shù)執(zhí)行順序的變化。JavaScript 引擎在 post-order traversal(后順序遍歷) (opens new window)中執(zhí)行模塊:先從模塊樹(shù)左側(cè)子樹(shù)開(kāi)始,模塊被執(zhí)行,導(dǎo)出它們的綁定,然后同級(jí)也被執(zhí)行,接著執(zhí)行父級(jí)。算法會(huì)遞歸運(yùn)行,直到執(zhí)行模塊樹(shù)的根節(jié)點(diǎn)。
在頂層 await 之前,此順序始終是同步的和確定性的:在代碼的多次運(yùn)行之間,可以保證代碼樹(shù)以相同的順序執(zhí)行。有了頂層 await 后,就存在相同的保證,除非你不使用頂層 await。
在模塊中使用頂層 await 時(shí):
等待 await 執(zhí)行完成后才會(huì)執(zhí)行當(dāng)前模塊。 子模塊執(zhí)行完 await,并且包括所有的同級(jí)模塊執(zhí)行完,并導(dǎo)出綁定,才會(huì)執(zhí)行父模塊。 假設(shè)代碼樹(shù)中沒(méi)循環(huán)或者其它 await ,同級(jí)模塊和父模塊,會(huì)以相同的同步順序繼續(xù)執(zhí)行。 在 await 完成后,被調(diào)用的模塊將繼續(xù)執(zhí)行 await。 只要沒(méi)有其他 await ,父模塊和子樹(shù)將繼續(xù)以同步順序執(zhí)行。
你可能會(huì)考慮的一些問(wèn)題
頂層 await 會(huì)阻斷執(zhí)行? 同級(jí)之間可以執(zhí)行,最終不會(huì)阻斷。 頂層 await 會(huì)阻斷資源請(qǐng)求。 頂層 await 發(fā)生在模塊圖的執(zhí)行階段,此時(shí)所有資源均開(kāi)始鏈接,沒(méi)有阻塞獲取資源的風(fēng)險(xiǎn)。 CommonJS 模塊沒(méi)有確定如何實(shí)現(xiàn)。 頂層 await 僅限于 ES 模塊,明確不支持 script 或 CommonJS 模塊。
點(diǎn)贊在看 無(wú) bug ??
