[英]Asynchronous function blocks the execution
據我了解,異步函數允許異步執行代碼,但在我的代碼中似乎不起作用:
async function longAsyncWork() {
await foo(); // takes about 10 seconds to complete
}
console.log('start');
longAsyncWork();
console.log('end');
在longAsyncWork完成后 ,在控制台中寫入了“ end”,但是我認為異步功能的目標是允許執行代碼而不會阻止主執行。
也許是console.log調用的特殊性使我看不到真相? 我可以假設在longAsyncWork函數完成之前執行longAsyncWork調用之后的代碼嗎?
Asnyc函數異步工作,但是javascript是單線程的,這意味着瀏覽器一次只能執行一項任務。 您實際上必須在函數內部進行異步操作才能使其真正起作用,但是您可能在其中進行了一些本質上是同步的阻塞操作。 讓我通過舉例更詳細地解釋一下:
async function fooThatCanBeAsync(){ var result = await fetch("https://stackoverflow.com"); console.log("after async task 1"); return result; } async function fooThatCannotBeAsync(){ var x = 0; for(i=0; i<10000000; i++){ x += i; } console.log("after async task 2"); } fooThatCanBeAsync(); fooThatCannotBeAsync(); console.log("end...");
上面這個愚蠢的例子有兩個異步函數。 但是,其中只有一個可以異步運行! 第一個功能嘗試通過發送http請求來獲取內容,這將需要一些時間。 這意味着,cpu現在是空閑的,直到它開始接收結果為止。 通過使此函數async
,您現在可以在兩次之間使用這段時間來執行其他任務。 這就是並發在javascript中的工作方式。
第二個函數執行for循環,進行一些計算。 這些計算不能是異步的,因為沒有辦法中斷它,沒有時間等待,也沒有I / O操作。 僅僅因為您添加了async
關鍵字,您就無法更改只有一個cpu線程正在執行該代碼的事實。
簡單地使函數async
並不意味着該函數內部的昂貴處理不會阻塞主線程-而是意味着該函數自動返回Promise,並且await
可以在其內部使用(除其他外)。 該函數執行的任何同步工作(包括該函數內部由await
調用完成的任何同步工作)仍將阻塞主線程。
const foo = () => new Promise((resolve) => { for (let i = 0; i < 1e9; i++) { } console.log('foo expensive sync work done'); resolve(); }); async function longAsyncWork() { await foo(); // takes about 10 seconds to complete } console.log('start'); longAsyncWork(); console.log('end, still on main thread');
longAsyncWork
調用解析為(或內部的任何await
)的最終承諾直到主線程完成后才解決,但是仍在阻塞任何await
之前的同步處理。
如果要確保昂貴的處理不會阻塞主線程,則可以使用網絡工作者代替:
const workerFn = () => { self.onmessage = () => { for (let i = 0; i < 1e9; i++) { } self.postMessage('done'); } }; const workerFnStr = `(${workerFn})();`; const blob = new Blob([workerFnStr], { type: 'text/javascript' }); const worker = new Worker(window.URL.createObjectURL(blob)); worker.onmessage = (result) => { console.log('result:', result.data); }; console.log('about to post message'); worker.postMessage('arg'); console.log('main thread continuing here');
您也可以撥打Promise.resolve()
運行前longAsyncWork
,但不同的是工人的版本,這仍然會阻止父窗口(不過,這將是主線程完成后 )的線程。 這通常是不可取的,因為它可以阻止用戶與頁面進行交互-網絡工作者通常是更好的選擇。
const foo = () => new Promise((resolve) => { for (let i = 0; i < 1e10; i++) { } console.log('foo expensive sync work done'); resolve(); }); async function longAsyncWork() { await foo(); // takes about 10 seconds to complete } console.log('start'); Promise.resolve() .then(longAsyncWork); console.log('end, still on main thread');
<p>Page won't display for a while, or if page is already displayed, scrolling won't work, because main thread is blocked</p>
您的代碼的有效版本。
const sleep = ms => new Promise(r => setTimeout(r, ms)) async function longAsyncWork() { // await resolves the sleep promise, so it lock longAsyncWork. await sleep(1000) console.log('async') } console.log('start'); // async longAsyncWork is a promise, so if you doesn't lock it with await, it will run async. longAsyncWork(); console.log('end');
按下[執行程式碼片段]即可查看其運作方式。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.