简体   繁体   English

异步功能阻止执行

[英]Asynchronous function blocks the execution

As far as I understand, an async function allows to execute code asynchronously but it doesn't seem to work in my code : 据我了解,异步函数允许异步执行代码,但在我的代码中似乎不起作用:

async function longAsyncWork() {
    await foo(); // takes about 10 seconds to complete
}

console.log('start');
longAsyncWork();
console.log('end');

The 'end' is written in the console after the longAsyncWork has completed but I thought that the goal of async functions was to allow to execute code without blocking the main execution. 在longAsyncWork完成 ,在控制台中写入了“ end”,但是我认为异步功能的目标是允许执行代码而不会阻止主执行。

Maybe it's a particularity of the console.log call that prevent me from seeing the truth? 也许是console.log调用的特殊性使我看不到真相? Can I assume that the code after the longAsyncWork call will be executed before the completion of the longAsyncWork function? 我可以假设在longAsyncWork函数完成之前执行longAsyncWork调用之后的代码吗?

Asnyc functions work asynchronously but javascript is single-threaded, which means the browser can do only one task at a time. Asnyc函数异步工作,但是javascript是单线程的,这意味着浏览器一次只能执行一项任务。 What you do inside the function must be asynchronous by nature to actually make it work, however you probably did some blocking operation there that is synchronous by nature. 您实际上必须在函数内部进行异步操作才能使其真正起作用,但是您可能在其中进行了一些本质上是同步的阻塞操作。 Let me explain in more details by giving example: 让我通过举例更详细地解释一下:

 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..."); 

The above silly example has two async functions. 上面这个愚蠢的例子有两个异步函数。 However, only one of them can run async! 但是,其中只有一个可以异步运行! The first function tries to fetch something by sending http request and it will take some time. 第一个功能尝试通过发送http请求来获取内容,这将需要一些时间。 This means, cpu is now free until it starts receiving the result. 这意味着,cpu现在是空闲的,直到它开始接收结果为止。 By making this function async , you can now use this time in between to do other tasks. 通过使此函数async ,您现在可以在两次之间使用这段时间来执行其他任务。 This is how concurrency works in javascript. 这就是并发在javascript中的工作方式。

The second function does a for loop, making some calculations. 第二个函数执行for循环,进行一些计算。 Those calculations cannot be async, because there is no way to interrupt it, no time to wait in between, and no i/o operation. 这些计算不能是异步的,因为没有办法中断它,没有时间等待,也没有I / O操作。 Just because you add async keyword, you can't change the fact that only one cpu thread is executing that code. 仅仅因为您添加了async关键字,您就无法更改只有一个cpu线程正在执行该代码的事实。

Simply making a function async does not mean that expensive processing inside that function will not block the main thread - rather, it'll mean that the function automatically returns a Promise, and that await can be used inside of it (among other things). 简单地使函数async并不意味着该函数内部的昂贵处理不会阻塞主线程-而是意味着该函数自动返回Promise,并且await可以在其内部使用(除其他外)。 Any synchronous work that the function does (including any synchronous work done by await calls inside the function) will still block the main thread. 该函数执行的任何同步工作(包括该函数内部由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'); 

The final Promise that the call of longAsyncWork resolves to (or any await s inside) will not resolve until after the main thread is finished, but synchronous processing before any await s have resolved will still block. longAsyncWork调用解析为(或内部的任何await )的最终承诺直到主线程完成后才解决,但是仍在阻塞任何await之前的同步处理。

If you want to ensure that expensive processing doesn't block the main thread, you can use a web worker instead: 如果要确保昂贵的处理不会阻塞主线程,则可以使用网络工作者代替:

 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'); 

You can also call Promise.resolve() before running longAsyncWork , but unlike the worker version, this will still block the thread of the parent window (though, it will be after the main thread has finished). 您也可以拨打Promise.resolve()运行前longAsyncWork ,但不同的是工人的版本,这仍然会阻止父窗口(不过,这将是主线程完成 )的线程。 This is often undesirable because it can prevent the user from interacting with the page - a web worker is often a better choice. 这通常是不可取的,因为它可以阻止用户与页面进行交互-网络工作者通常是更好的选择。

 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> 

A working version of your code. 您的代码的有效版本。

 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'); 

press Run code snippet to see it working. 按下[执行程式码片段]即可查看其运作方式。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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