我目前正在研究处理NodeJS同步和异步函数的事件循环。从我的理解来看,同步函数会先执行,然后是异步函数。在下面的例子中,控制台日志同步执行,直到我从“fetchAPI”函数中删除await,那么同步函数将首先在“eventLoop”函数中执行,然后是异步函数。我试图理解为什么会发生这种行为。我错过了什么?
const experiment = async () =>{
const num = 1
setTimeout(()=>console.log("Hello from timeout"),0)
console.log(num)
const fetchAPI = await fetch("https://xxxxxxxxxxxx").then(x=>{
console.log("Hello from API")
return x.json()
})
console.log(fetchAPI.id)
console.log("String")
Promise.resolve().then(()=>console.log("In promise"))
}
experiment()
API“https://xxxxxxxxxx”将返回一个随机数(以下示例中为139)。上一个函数的输出为:
1
Hello from timeout
Hello from API
139
String
In promise
每当我从函数“fetchAPI”中删除await时,它将输出:
1
undefined
String
In promise
Hello from timeout
Hello from API
1条答案
按热度按时间lpwwtiir1#
fetch()
前面的await
告诉Javascript,你想挂起包含函数的执行,直到fetch()
返回的promise满足为止。这就是为什么fetch()
后面的代码在fetch()
完成之前都不会执行。如果你删除了
await
,那么fetch()
会发送它的请求,然后立即返回,包含它的函数会继续执行其他语句。当fetch()
在未来某个时间完成时,你注册了.then()
处理程序的promise会在没有其他任何东西运行时通过事件系统调度.then()
处理程序运行。因此,
await
将挂起experiment()
函数的进一步执行,直到fetch().then()
promise实现。没有await
,experiment()
函数将继续运行,而无需等待fetch()
操作完成。当你让你的代码返回到事件循环时,
setTimeout(fn, 0)
就会运行。当你使用await fetch()
时,它会把解释器发送回事件循环,因此计时器会快速运行。当不使用
await
时,experiment()
函数的其余部分将在控制返回事件循环之前执行,因此计时器回调将延迟到以后。Promise.resolve().then(()=>console.log("In promise"))
在计时器之前运行,因为准备好运行其处理程序的promise在通用事件循环(如计时器)之前提供(如果两者都准备好运行,则可以认为promise具有更高的优先级)。fetch().then()
不会立即运行,因为它有实际的异步工作要做,在它解析其promise并准备运行之前需要实际的时间,而Promise.resolve().then(...)
立即解析其promise,因此立即准备运行。**注意:**尝试理解所有这些是完全可以的,但99%的情况下,你不应该依赖于事件循环如何优先处理各种等待运行的事情的细节。如果你想控制操作的顺序,你通常应该编写代码来强制事情按照该顺序执行,而不依赖于事件循环的微小时间细节。
在现代代码中,将多个异步操作按期望的执行顺序排序的简单方法是确保所有操作都使用promise(promisize任何还没有使用promise的东西),然后使用
await
或.then().then()
promise链对事情进行排序。await
对于排序异步操作来说是最简单的,因为在异步操作之间进行条件分支更简单,我发现更容易实现你想要的任何类型的错误处理,无论是中央或每个操作或两者的某种组合。注意,nodejs已经有了一个基于承诺的
setTimeout()
版本。