[英]Why an async function takes more time to execute than a sync one?
我寫了兩個遞歸函數,它們對數組中的數字求和。 他們做同樣的事情,一個是異步的,另一個是同步的。 異步函數花費的時間大約是同步函數的9倍。
異步函數不應該利用同時運行更多任務的事實嗎?
功能
// Asynchronously sum the numbers in array
async function sumAsync(arr){
if(arr.length == 1) return arr[0];
const half = arr.length/2;
// Numbers on the left half
const left = arr.filter((e, i) => {
return i < half;
});
// Numbers on the right half
const right = arr.filter((e, i) => {
return i >= half;
});
// Recursive call
const leftR = sumAsync(left);
const rightR = sumAsync(right);
// Wait for resolves
await Promise.all([leftR, rightR]);
return await leftR + await rightR;
}
// Synchronously sum the numbers in array
function sumSync(arr){
if(arr.length == 1) return arr[0];
const half = arr.length/2;
// Numbers on the left half
const left = arr.filter((e, i) => {
return i < half;
});
// Numbers on the right half
const right = arr.filter((e, i) => {
return i >= half;
});
// Recursive call
const leftR = sumSync(left);
const rightR = sumSync(right);
return leftR + rightR;
}
測試它們
(async () => {
const LENGTH = 1048576; // 1024^2
const arr = Array.from(Array(LENGTH), num => Math.random()*10 | 0);
// arr[1048576] <- random (0 ~ 9)
// Async sum
console.log('ASYNC');
before = Date.now();
console.log(`SUM: ${await sumAsync(arr)}`);
after = Date.now();
console.log(`TIME: ${after - before} ms`);
// Sync sum
console.log('SYNC');
before = Date.now();
console.log(`SUM: ${sumSync(arr)}`);
after = Date.now();
console.log(`TIME: ${after - before} ms`);
})();
結果
// ASYNC
// SUM: 4720832
// TIME: 5554 ms
// SYNC
// SUM: 4720832
// TIME: 613 ms
async
函數的返回值始終是一個 Promise,即使該函數只執行同步操作,而 Promise 的await
(或.then
)只會在微任務期間(在當前同步代碼運行完畢后)運行其后的內容)。 對於大型數組,這將導致包裝同步代碼的許多不必要的微任務。
當沒有實際的異步發生時,這只是額外的負擔,並導致需要額外的處理時間和功率。
異步函數不應該利用同時運行更多任務的事實嗎?
Javascript 是單線程的,即使有async
函數。 如果一次調用多個異步函數,則在任何時候只有一條通過代碼的路徑可能是“活動的”。 如果所有任務所需的總處理時間是 1000 毫秒,那么在標准 Javascript 中,就不可能花費至少 1000 毫秒。
你實際上並沒有同時運行更多的任務——你只是在做同樣的工作時(不必要地)將任務包裝在 Promises 中。
對於真正的並行操作,您必須使用當前環境提供的內容,例如 Node 中的child_process或web worker 。
簡短版本:異步一次只做一件事。 它在隊列中的任務之間切換(每個交換機都有開銷),當一個任務阻塞時,它將控制權交給另一個任務(交換機開銷,並在解除阻塞時重新排隊被阻塞的任務)。
長版:異步並不意味着並行處理,它意味着交錯(並發、協作)處理。 即使使用異步,JavaScript 仍然是單線程的,並且您所做的所有實際工作都完全受 CPU 限制。 事實上,你唯一真正的並發是異步代碼將重復調度、暫停和恢復你的遞歸調用(但仍然一次只做一個),而同步代碼只會盡可能快地按順序執行它們,不涉及事件循環。
異步代碼的好處是,當正在執行阻塞 I/O(包括等待用戶輸入之類的事情)時,該任務可以暫停,直到它被一些帶外信號(I/O 完成,用戶點擊鼠標)解除阻塞,無論如何),其他任務可以運行。 好處是在大多數任務大部分時間都在等待某些事情的情況下獲得並發(但不是並行)處理的好處,因此准備運行的少數可以立即開始運行(並且因為它們通常不運行,任務切換的開銷並不重要;大多數時候,沒有什么可切換的,當你無事可做時,就會支付這么多開銷)。 但這絕對比不暫停地進行數字運算要高得多。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.