Javascript 将 async/await 阻塞一个线程 node.js
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/46004290/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Will async/await block a thread node.js
提问by rajesh_pudota
When async/awaitused in a node.js function, will it block the node.js thread untill it executes the next line of code ?
当async/await在一个的node.js函数使用时,它会阻塞的node.js线程,直到它执行代码的下一行?
回答by jfriend00
async/awaitdoes not block the whole interpreter. node.js still runs all Javascript as single threaded and even though some code is waiting on an async/await, other events can still run their event handlers (so node.js is not blocked). The event queue is still being serviced for other events. In fact, it will be an event that resolves a promise that will allow the awaitto stop awaiting and run the following code.
async/await不会阻塞整个解释器。node.js 仍然将所有 Javascript 作为单线程运行,即使某些代码正在等待async/await,其他事件仍然可以运行它们的事件处理程序(因此 node.js 不会被阻止)。事件队列仍在为其他事件服务。事实上,这将是一个解决承诺的事件,该承诺将允许await停止等待并运行以下代码。
Code like this:
像这样的代码:
await foo(); // foo is an async function that returns a promise
console.log("hello");
is analogous to this:
类似于:
foo().then(() => {
console.log("hello");
});
So, awaitjust puts the following code in that scope into an invisible .then()handler and everything else works pretty much the same as if it was actually written with a .then()handler.
因此,await只需将该范围内的以下代码放入一个不可见的.then()处理程序中,其他所有内容的工作方式与实际使用.then()处理程序编写的几乎相同。
So, awaitallows you to save the writing of the .then()handler and gives the code a synchronous look to it (though it isn't really synchronous). In the end it's a shorthand that lets you write async code with fewer lines of code. One does need to remember though that any promise that can reject must have a try/catch somewhere around it to catch and handle that rejection.
因此,await允许您保存.then()处理程序的编写并为代码提供同步外观(尽管它不是真正同步)。最后,它是一种速记,可让您用更少的代码行编写异步代码。确实需要记住,任何可以拒绝的承诺都必须在它周围的某个地方有一个 try/catch 来捕获和处理该拒绝。
Logically, you can think of what node.js does when it encounters an awaitkeyword when executing a function as the following:
从逻辑上讲,你可以想象 node.jsawait在执行函数时遇到关键字时会做的事情如下:
- Function call is made
- The interpreter sees that the function is declared as
asyncwhich means that it will always return a promise. - The interpreter starts executing the function.
- When it encounters an
awaitkeyword, it suspends further execution of that function until the promise that is being awaited resolved. - The function then returns an unresolved promise.
- At this point, the interpreter continues executing whatever comes after the function call (usually a
fn().then()is followed by other lines of code). The.then()handlers are not executed yet because the promise is not yet resolved. - At some point this sequence of Javascript finishes and returns control back to the interpreter.
- The interpreter is now free to serve other events from the event queue. The original function call that ran into an
awaitkeyword is still suspended, but other events can be processed now. - At some future point, the original promise that was being awaited gets resolved. When it's time for that to get processed in the event queue, the previously suspended function continues executing on the line after the
await. If there are any moreawaitstatements, then the function execution is again suspended until that promise resolves. - Eventually the function hits a
returnstatement or reaches the end of the function body. If there is areturn xxxstatement, then thexxxis evaluated and its result becomes the resolved value of the promise that thisasyncfunction has already returned. The function is now done executing and the promise it previously returned has been resolved. - This will cause any
.then()handlers attached to the promise that this function previously returned to get called. - After those
.then()handlers run, the job of thisasyncfunction is finally done.
- 进行了函数调用
- 解释器看到函数被声明为
async这意味着它总是会返回一个承诺。 - 解释器开始执行函数。
- 当它遇到一个
await关键字时,它会暂停该函数的进一步执行,直到正在等待的承诺得到解决。 - 然后该函数返回一个未解决的承诺。
- 此时,解释器继续执行函数调用之后的任何内容(通常 a
fn().then()后跟其他代码行)。的.then(),因为承诺尚未解决处理程序尚未执行。 - 在某个时候,这个 Javascript 序列完成并将控制权返回给解释器。
- 解释器现在可以自由地为事件队列中的其他事件提供服务。遇到
await关键字的原始函数调用仍然暂停,但现在可以处理其他事件。 - 在未来的某个时刻,正在等待的原始承诺得到解决。当需要在事件队列中处理它时,先前挂起的函数会继续在
await. 如果还有更多await语句,则函数执行将再次暂停,直到该承诺解决为止。 - 最终,函数命中一个
return语句或到达函数体的末尾。如果有return xxx语句,则xxx计算 并且其结果成为此async函数已经返回的承诺的已解析值。该函数现在已完成执行,并且它先前返回的承诺已得到解决。 - 这将导致任何
.then()附加到此函数先前返回的承诺的处理程序被调用。 - 在这些
.then()处理程序运行之后,这个async函数的工作终于完成了。
So, while the whole interpreter doesn't block (other Javascript events can still be serviced), the execution of the specific asyncfunction that contains the awaitstatement was suspended until the promise that was being awaited resolved. What's important to understand is step 5 above. When the first awaitis hit, the function immediately returns an unresolved promise and code after this function is executed (before the promise being awaitedis resolved). It's for this reason that the whole interpreter is not blocked. Execution continues. Only the insides of one function are suspended until a promise is resolved.
因此,虽然整个解释器不会阻塞(其他 Javascript 事件仍然可以被服务),但async包含该await语句的特定函数的执行被挂起,直到正在等待的承诺得到解决。重要的是要理解上面的第 5 步。当第一个await被命中时,该函数立即返回一个未解析的承诺和代码,在该函数执行后(在承诺被awaited解析之前)。正是因为这个原因,整个解释器才不会被阻塞。执行继续。只有一个函数的内部被挂起,直到一个 promise 被解决。
回答by trincot
async/awaitis just syntactic sugar for thencalls on a promise. Nor Promises, nor asyncnor awaitcreate new threads.
async/await只是then调用 promise 的语法糖。也承诺,也没有async也没有await创建新的线程。
When awaitis executed, the expression that follows it is evaluated synchronously. It should be a promise, but if it is not, it is wrapped into one, as if you had await Promise.resolve(expression).
当await被执行时,它后面的表达被同步评估。它应该是一个承诺,但如果不是,它就被包装成一个,就好像你有await Promise.resolve(expression).
Once that expression is evaluated, the asyncfunction returns -- it returns a promise. Then code execution continues whith whatever code follows that function call (same thread) until the call stack is empty.
一旦表达式被求值,async函数就会返回——它返回一个promise。然后代码执行继续执行该函数调用(同一线程)之后的任何代码,直到调用堆栈为空。
At some point the promise that was evaluated for the awaitwill resolve. This will put a microtask in the microtask queue. When the JavaScript engine has nothing more to do in the current task, it will consume the next event in the microtask queue. As this microtask involves a resolved promise, it will restore the previous execution state of the asyncfunction and continue with whatever comes next after the await.
在某些时候,为await遗嘱评估的承诺会解决。这会将微任务放入微任务队列中。当 JavaScript 引擎在当前任务中无事可做时,它将消耗微任务队列中的下一个事件。由于此微任务涉及已解决的承诺,因此它将恢复async函数的先前执行状态并继续执行await.
The function may execute other awaitstatements, with similar behaviour, although the function now no longer returns to where it was originally called from (as that call was already processed with the first await), it merely returns leaving the call stack empty, and leaves the JavaScript engine to process the microtask and task queues.
该函数可以执行await具有类似行为的其他语句,尽管该函数现在不再返回到最初调用它的位置(因为该调用已经用第一个 处理过await),它只是返回使调用堆栈为空,并保留 JavaScript引擎来处理微任务和任务队列。
All this happens with the same thread.
所有这些都发生在同一个线程中。
回答by John Deighan
I just had an "aha!" moment and thought that I'd pass it on. "await" does not return control directly to JavaScript - it returns control to the caller. Let me illustrate. Here is a program using callbacks:
我只是“啊哈!” 片刻,并认为我会传递它。“await”不会直接将控制权返回给 JavaScript - 它会将控制权返回给调用者。让我举例说明。这是一个使用回调的程序:
console.log("begin");
step1(() => console.log("step 1 handled"));
step2(() => console.log("step 2 handled"));
console.log("all steps started");
// ----------------------------------------------
function step1(func) {
console.log("starting step 1");
setTimeout(func, 10000);
} // step1()
// ----------------------------------------------
function step2(func) {
console.log("starting step 2");
setTimeout(func, 5000);
} // step2()
The behavior that we want is 1) both steps are immediately started, and 2) when a step is ready to be handled (imagine an Ajax request, but here we just wait for some period of time), the handling of each step happens immediately.
我们想要的行为是 1) 两个步骤都立即开始,2) 当一个步骤准备好被处理时(想象一个 Ajax 请求,但这里我们只是等待一段时间),每个步骤的处理立即发生.
The "handling" code here is console.log("step X handled"). That code (which in a real application can be quite long and possibly include nested awaits), is in a callback, but we'd prefer for it to be top level code in a function.
这里的“处理”代码是 console.log(“step X 处理”)。该代码(在实际应用程序中可能很长并且可能包含嵌套的等待)位于回调中,但我们更希望它是函数中的顶级代码。
Here is equivalent code using async/await. Note that we had to create a sleep() function since we need to await on a function that returns a promise:
这是使用 async/await 的等效代码。请注意,我们必须创建一个 sleep() 函数,因为我们需要等待一个返回承诺的函数:
let sleep = ms => new Promise((r, j)=>setTimeout(r, ms));
console.log("begin");
step1();
step2();
console.log("all steps started");
// ----------------------------------------------
async function step1() {
console.log("starting step 1");
await sleep(10000);
console.log("step 1 handled");
} // step1()
// ----------------------------------------------
async function step2() {
console.log("starting step 2");
await sleep(5000);
console.log("step 2 handled");
} // step2()
The important takeaway for me was that the await in step1() returns control to the main body code so that step2() can be called to start that step, and the await in step2() also returns to the main body code so that "all steps started" can be printed. Some people advocate that you use "await Promise.all()" to start multiple steps, then after that, handle all steps using the results (which will appear in an array). However, when you do that, no step is handled until all steps resolve. That's not ideal, and seems to be totally unnecessary.
对我来说重要的一点是,step1() 中的 await 将控制权返回给主体代码,以便可以调用 step2() 来开始该步骤,而 step2() 中的 await 也返回主体代码,以便“所有步骤已开始”可以打印。有人主张你使用“await Promise.all()”来开始多个步骤,然后使用结果(会出现在一个数组中)处理所有步骤。但是,当您这样做时,在所有步骤都解决之前不会处理任何步骤。这并不理想,而且似乎完全没有必要。
回答by mikep
Will async/await block a thread node.js? As @Nidhin David said it depends on what code you have inside async function - db calls, network calls, filesystem calls are not blocking but blocking are for example long for/while cycles, JSON stringify/parse and evil/vulnerable regular expressions (google for ReDoS Attacks). Each of four examples below will block the main thread if /testrequest is called because of code string.match(/^(a|a)+$/)is synchronous and takes long time to process.
async/await 会阻塞线程 node.js 吗?正如@Nidhin David 所说,这取决于您在异步函数中的代码 - db 调用、网络调用、文件系统调用不会阻塞,但阻塞是例如 long for/while 循环、JSON stringify/parse 和 evil/vulnerable 正则表达式(谷歌用于 ReDoS 攻击)。如果/test请求被调用,以下四个示例中的每一个都会阻塞主线程,因为代码string.match(/^(a|a)+$/)是同步的并且需要很长时间来处理。
This first example will block main node thread as expected and no other requests/clients can be served.
第一个示例将按预期阻塞主节点线程,并且无法为其他请求/客户端提供服务。
var http = require('http');
// This regexp takes to long (if your PC runs it fast, try to add some more "a" to the start of string).
// With each "a" added time to complete is always doubled.
// On my PC 27 times of "a" takes 2,5 seconds (when I enter 28 times "a" it takes 5 seconds).
// https://en.wikipedia.org/wiki/ReDoS
function evilRegExp() {
var string = 'aaaaaaaaaaaaaaaaaaaaaaaaaaab';
string.match(/^(a|a)+$/);
}
// Request to http://localhost:8080/ wil be served quickly - without evilRegExp() but request to
// http://localhost:8080/test/ will be slow and will also block any other fast request to http://localhost:8080/
http.createServer(function (req, res) {
console.log("request", req.url);
if (req.url.indexOf('test') != -1) {
console.log('runing evilRegExp()');
evilRegExp();
}
res.write('Done');
res.end();
}).listen(8080);
You can run many parallel requests to http://localhost:8080/and it will be fast. Then run just one slow request http://localhost:8080/test/and no other request (even those fast at http://localhost:8080/) will not be served until slow (blocking) request ends.
您可以对http://localhost:8080/运行许多并行请求,而且速度会很快。然后只运行一个慢速请求http://localhost:8080/test/并且在慢速(阻塞)请求结束之前不会提供其他请求(即使是那些在http://localhost:8080/处快速的请求)。
This second example uses promises but it still block main node thread and no other requests/clients can be served.
第二个示例使用了 Promise,但它仍会阻塞主节点线程,并且无法为其他请求/客户端提供服务。
var http = require('http');
function evilRegExp() {
return new Promise(resolve => {
var string = 'aaaaaaaaaaaaaaaaaaaaaaaaaaab';
string.match(/^(a|a)+$/);
resolve();
});
}
http.createServer(function (req, res) {
console.log("request", req.url);
if (req.url.indexOf('test') != -1) {
console.log('runing evilRegExp()');
evilRegExp();
}
res.write('Done');
res.end();
}).listen(8080);
This third example uses async+await but it is also blocking (async+await is the same as native Promise).
第三个示例使用 async+await 但它也是阻塞的(async+await 与原生 Promise 相同)。
var http = require('http');
async function evilRegExp() {
var string = 'aaaaaaaaaaaaaaaaaaaaaaaaaaab';
string.match(/^(a|a)+$/);
resolve();
}
http.createServer(function (req, res) {
console.log("request", req.url);
if (req.url.indexOf('test') != -1) {
console.log('runing evilRegExp()');
await evilRegExp();
}
res.write('Done');
res.end();
}).listen(8080);
Fourth example uses setTimeout() which causes slow request seems to be served immediately (browser quickly gets "Done") but it is also blocking and any other fast requests will wait until evilRegExp() ends.
第四个示例使用 setTimeout() 导致慢速请求似乎立即得到服务(浏览器很快“完成”),但它也被阻塞,任何其他快速请求将等到 evilRegExp() 结束。
var http = require('http');
function evilRegExp() {
var string = 'aaaaaaaaaaaaaaaaaaaaaaaaaaab';
string.match(/^(a|a)+$/);
}
http.createServer(function (req, res) {
console.log("request", req.url);
if (req.url.indexOf('test') != -1) {
console.log('runing evilRegExp()');
setTimeout(function() { evilRegExp(); }, 0);
}
res.write('Done');
res.end();
}).listen(8080);
回答by Nidhin David
As long as the code contained inside the async/await is non blocking it won't block, for example db calls, network calls, filesystem calls.
只要包含在 async/await 中的代码是非阻塞的,它就不会阻塞,例如数据库调用、网络调用、文件系统调用。
But if the code contained inside async/await is blocking, then it will block the entire Node.js process, for example infinite loops, CPU intensive tasks like image processing, etc.
但是如果包含在 async/await 中的代码是阻塞的,那么它将阻塞整个 Node.js 进程,例如无限循环、图像处理等 CPU 密集型任务等。
In essence async/await is a language level wrapper around Promises so that the code can have a synchronous 'look and feel'
本质上,async/await 是 Promises 的语言级包装器,因此代码可以具有同步的“外观和感觉”
回答by Rizwan Ansar
Async functions enable us to write promise based code as if it were synchronous, but without blocking the execution thread. It operates asynchronously via the event-loop. Async functions will always return a value. Using async simply implies that a promise will be returned, and if a promise is not returned, JavaScript automatically wraps it in a resolved promise with its value.
异步函数使我们能够编写基于 Promise 的代码,就像它是同步的一样,但不会阻塞执行线程。它通过事件循环异步运行。异步函数将始终返回一个值。使用 async 只是意味着将返回一个承诺,如果没有返回一个承诺,JavaScript 会自动将它包装在一个已解决的承诺中,并带有它的值。
Find the article on Medium . How to use Async Await in JavaScript.
在 Medium 上查找文章。 如何在 JavaScript 中使用 Async Await。

