10

我一直在使用TypeScript和原生Promise在SPA中使用功能,我注意到,即使我將一個長時間運行的函數重構爲一個返回promise的異步函數,UI仍然沒有響應。異步在瀏覽器中是否真正無阻塞?

所以我的問題是:

  • 究竟怎樣新的異步/等待功能有助於避免瀏覽器阻塞UI?當使用async/await實際獲得響應式用戶界面時,是否需要採取特殊的額外步驟?

  • 有人可以創建一個小提琴來演示async/await如何幫助使UI響應嗎?

  • 異步/等待如何與先前的異步功能(如setTimeout和XmlHttpRequest)相關?

+4

代碼塊仍然會阻塞。如果沒有,你可以進行數據競賽。異步函數的思想是你可以暫停等待稍後執行異步代碼。所以你在等待異步完成時停下來,比如'setTimeout',一個XHR響應或事件一個點擊事件:https://jsfiddle.net/wgqyayhr/ *(Demo需要支持的瀏覽器)* –

+0

'異步/ await'是**不是** ES7(ES2016)的一部分。這將成爲今年發佈的ES2017的一部分。 –

回答

22

await p當承諾p解決時,計劃執行其餘功能的執行。就這樣。可以使用await。這(幾乎)它所做的一切(它也將你的結果包含在承諾中)。

他們一起使非阻塞代碼讀取像更簡單的阻止代碼。他們不會解除對代碼的阻止。

對於響應UI,卸載CPU密集型的工作到worker螺紋,和傳遞消息給它:

async function brutePrime(n) { 
 
    function work({data}) { 
 
    while (true) { 
 
     let d = 2; 
 
     for (; d < data; d++) { 
 
     if (data % d == 0) break; 
 
     } 
 
     if (d == data) return self.postMessage(data); 
 
     data++; 
 
    } 
 
    } 
 

 
    let b = new Blob(["onmessage =" + work.toString()], {type: "text/javascript"}); 
 
    let worker = new Worker(URL.createObjectURL(b)); 
 
    worker.postMessage(n); 
 
    return await new Promise(resolve => worker.onmessage = e => resolve(e.data)); 
 
} 
 

 
(async() => { 
 
    let n = 700000000; 
 
    for (let i = 0; i < 10; i++) { 
 
    console.log(n = await brutePrime(n + 1)); 
 
    } 
 
})().catch(e => console.log(e));

+4

沒有外部文件的工人的積分。這是一個很酷的伎倆。 – solarc

5

async是一種更優雅的方式來構造異步代碼。它不允許任何新的功能;它只是比回調或承諾更好的語法。

因此,async不能用於「使某些異步」。如果你的代碼需要進行大量的基於CPU的處理,那麼async不會神奇地讓UI響應。你需要做的是使用類似web workers的東西,其中正確的工具將CPU綁定的工作推到後臺線程以使UI響應。

+0

我想你可以像這樣使用它來阻止循環阻塞。 爲(令i = 0; I <100000;我++){ 等待延遲(10) } 異步功能延遲(ms)的{ 返回新無極((解析,拒絕))=>的setTimeout(解析,女士)); } –

3

JavaScript是單線程並在相同的線程中運行UI。所有的JavaScript代碼都會阻止用戶界面。正如其他人所提到的,網絡工作者可以用來在其他線程中運行代碼,但是它們有侷限性。

異步函數和常規函數之間的區別是它們返回一個承諾。然後使用回調,可以推遲執行代碼,該代碼處理函數調用的結果,從而允許UI執行一些工作。下面的三個例子具有相同的效果:

async function foo() { 
    console.log("hi"); 
    return 1; 
} 
foo().then(result => console.log(result)) 
console.log("lo"); 

function foo() { 
    console.log("hi"); 
    return 1; 
} 
Promise.resolve(foo()).then(result => console.log(result)) 
console.log("lo"); 

function foo() { 
    console.log("hi"); 
    return 1; 
} 
const result = foo(); 
setTimeout(() => console.log(result)); 
console.log("lo"); 

在所有這三種情況下,控制檯日誌HI,LO,1。1之前被印刷在UI可以處理用戶輸入或繪製更新。在前兩種情況中最後打印的原因是,承諾回調不會立即執行。

await允許你這樣做,如果沒有回調:

async function foo() { 
    console.log("hi"); 
    return 1; 
} 

async function bar() { 
    const result = await foo(); 
    console.log(result); 
} 

bar(); 
console.log("lo"); 

這也將打印HI,LO,1很像一個承諾回調,await後的代碼永遠不會立即執行。