Javascript 异步函数在Nodejs中的行为不符合预期
我有一个简单的代码:Javascript 异步函数在Nodejs中的行为不符合预期,javascript,node.js,async-await,es6-promise,Javascript,Node.js,Async Await,Es6 Promise,我有一个简单的代码: (async ()=>{ const five = await printHello() console.log(`five: ${five}`); })() async function printHello(){ console.log('Hello World') return 5; } console.log("App started"); 我的期望是,它应该打印: App started Hello World
(async ()=>{
const five = await printHello()
console.log(`five: ${five}`);
})()
async function printHello(){
console.log('Hello World')
return 5;
}
console.log("App started");
我的期望是,它应该打印:
App started
Hello World
five: 5
因为执行的匿名函数被标记为异步
但是,即使经过多次试验,它也会打印:
Hello World
App started
five: 5
有人能解释一下为什么会这样吗?首先,你可以调用
printHello()
然后调用console.log('helloworld')
哪个日志
然后返回5代码>它解决了承诺
然后您const five=wait…
等待上面返回的承诺。这是一个微任务,因此它排队,异步函数进入睡眠状态
IIFE完成运行,因此外部功能继续执行console.log(“应用程序启动”)代码>应用程序启动的日志
然后外部函数完成,因此微任务被从队列中拉出,异步函数被唤醒
来自promise的解析值被赋值并console.log(`five:${five}`)代码>记录它
有关详细信息,请参阅。async
函数返回承诺,但async
函数中的同步代码立即执行(即同步执行)。
await
语句暂停代码执行,直到给出给它的承诺得到解决或拒绝
我总是比较async
函数行为和Promise
的行为<代码>执行器
函数立即被调用,即同步调用。您的代码没有执行任何异步操作。printHello
方法同步执行。
在函数前面加上async
,只需确保它返回一个承诺即可。您可以用callback替换它,它将以相同的方式工作,因为callback将立即返回,而无需等待事件循环的下一个滴答声
因此,如果您希望代码按照您希望的方式运行,那么您必须使用setTimeout或setImmediate调用来包装代码
async function printHello() {
setImmediate(() => {
console.log('Hello World');
return 5;
});
}
您的代码不是异步的。这就是问题所在。您需要返回一个始终异步的承诺
const users = [
{
id: 0,
name: 'foo',
},
{
id: 1,
name: 'bar',
}
];
function getUserById(id) {
return new Promise((resolve, reject) => {
setTimeout(function() {
resolve(users.filter(user => user.id === id)[0]);
}, 1000);
});
}
async function getUser(id) {
let name;
await getUserById(id).then(function(user) {
name = user ? user.name : null;
});
console.log(name);
}
getUser(0);
console.log('console');
所有人都给出了很多有用的答案,但我需要为此写一个答案,以帮助那些有着与我类似想法的人:仅仅将函数标记为异步[或将其包装为承诺]不会使CPU密集型代码在nodejs中以异步方式运行
在遇到这个问题之前,我的理解是,我们可以将异步放在函数之前,并将所有繁重的任务放在函数上。Aysnc将介绍如何在完成[返回]后让nodejs事件循环知道。我过去认为它通过繁殖线程在引擎盖下完成所有事情
但这种理解是错误的。节点是单线程的,如果您要编写代码的话。永远不要把任何繁重的任务放在nodej身上
考虑一下这个express应用程序:
const express = require('express')
const app = express()
app.use('/name/:name',(req,res)=>{
if(req.params['name']==='John'){
res.end(timeConsumingFunct(2000).toString())
}else{
res.end(timeConsumingFunct(10000).toString())
}
})
function timeConsumingFunct(ms) {//simulating CPU intensive work
var start = Date.now(),
now = start;
while (now - start < ms) {
now = Date.now();
}
return ms/1000;
}
app.listen(3000,()=>{
console.log('Server started at : http://localhost:3000')
})
const express=require('express'))
const app=express()
应用程序使用('/name/:name',(请求,请求)=>{
如果(请求参数['name']='John'){
res.end(timeConsumingFunct(2000).toString())
}否则{
res.end(timeConsumingFunct(10000).toString())
}
})
函数timeConsumingFunct(ms){//模拟CPU密集型工作
var start=Date.now(),
现在=开始;
同时(现在-开始<毫秒){
now=Date.now();
}
返回ms/1000;
}
app.listen(3000,()=>{
console.log('服务器启动于:http://localhost:3000')
})
现在,如果您访问:http://localhost:3000/name/Tom
首先,然后http://localhost:3000/name/John
在您的浏览器中,John先生至少需要等待12秒。
这是因为节点js是单线程的,无论您使用async还是承诺包装timeConsumingFunct
,它都会挂起整个流
唯一真正的异步方法是使用子进程或WorkAPI,或者用C++编写。
作为JS编写的任何东西都是同步的,它会挂断所有请求,在您认为可以在nodejs中进行任何操作之前,请先了解这一点。这可能是因为代码是自上而下执行的,因此在“应用程序启动”之前会显示“Hello World”消息。@Edric是的,但当您将其标记为async时,调用转到下一行,在事件循环的下一个刻度上执行async funct。那么立即执行的匿名异步函数呢?它应该创建微任务并排队等待下一个滴答声。”“应用程序已启动”表示当前勾号。纯形式的Promise会这样做,但async
函数会暂停所有操作。所以最后一行等待IIFE完成。@user3769778-调用异步函数不会创建微任务。如果我在printHello中有一个非常大的计算,我希望它不会挂断我的事件循环,那么我有什么选项可以使它异步?我认为将任何函数标记为async都将使其异步,传递将转到下一行。将其包装在setImmediate或process.nextTick中将使其显示为异步,并且不会立即阻止执行。但是在子进程中执行它将是一个更好的主意(),虽然代码没有做任何异步的事情,但它仍然是异步的,因为承诺(也是从异步函数返回的承诺)保证了异步解析。你是对的,不过转移到另一个流程似乎是正确的方法。