繁体   English   中英

异步功能阻止执行

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM