异步等待 - 等待是否阻止其他代码运行?

时间:2020-12-28 13:20:45

标签: javascript async-await

在 javascript 中,await 块代码吗?例如,假设我们有以下代码:

async function queryDB() {
    const addUser = await promisePool.execute("INSERT INTO Users (User) VALUES ('username')")
    const selectUser = await promisePool.execute("SELECT User FROM Users") 
}

“selectUser”是否会等到 addUser 完成后才运行,以便我们可以选择添加的用户?

另外,假设我们在等待之间添加了一些不是承诺的代码,如下所示:

    async function queryDB() {
        const addUser = await promisePool.execute("INSERT INTO Users (User) VALUES ('username')")

setTimeout(() => console.log('Do something that takes 3 seconds'), 3000);

        const selectUser = await promisePool.execute("SELECT User FROM Users") 
    }

“selectUser”会等待 addUser 而不是 setTimeout 吗?如果是这样,你如何编写上面的代码,让 addUser 先运行,然后 setTimeout 再 selectUser?

我还想补充一点,我一直在研究和阅读 stackoverflow 和其他资源,但我需要澄清一下。

5 个答案:

答案 0 :(得分:4)

<块引用>

“selectUser”会等到 addUser 完成后才运行,以便我们可以 选择添加的用户?

来自MDN Docs - await:

<块引用>

await 表达式导致异步函数执行暂停,直到 Promise 已解决(即完成或拒绝),并恢复 完成后执行异步函数。复工时, await 表达式的值是已完成的 Promise 的值。

queryDB 函数的执行将在等待第一个 promise 完成时暂停。第二行代码只有在第一行成功完成后才会执行。

<块引用>

“selectUser”会等待 addUser 而不是 setTimeout 吗?

是的,没错。

<块引用>

如果是这样,你会怎么写上面的代码让addUser先运行,然后 setTimeout 然后选择User?

您可以将 setTimeout 包装在一个返回承诺的函数中,然后等待该承诺以确保在前两行完成后执行最后一行。

以下是包装setTimeout的包装函数示例

function waitForTimeout(seconds) {
  return new Promise((resolve, reject) => {
     setTimeout(() => { 
        console.log("Hello World");
        resolve();
     }, seconds * 1000);        
  });
}

一旦你有了一个包装函数,你就可以await它,如下所示:

async function queryDB() {
    const addUser = await promisePool.execute(
        "INSERT INTO Users (User) VALUES ('username')"
    );

    await waitForTimeout(3);    // wait for 3 seconds

    const selectUser = await promisePool.execute("SELECT User FROM Users") 
}

答案 1 :(得分:1)

简短的回答是肯定的,它会阻塞。 await 的目的是确保您在下一行中有可用的结果。因此,如果您需要 addUser 的值来进行下一个查询,您将拥有它。

引入它是为了让您不必在第一行的末尾编写 then() 并将第二个调用放入回调中,这会使代码难以阅读。

答案 2 :(得分:0)

“selectUser”是否会等到 addUser 完成后才运行,以便我们可以选择添加的用户?

是的。

“selectUser”会等待 addUser 而不是 setTimeout 吗?如果是这样,你如何编写上面的代码,让 addUser 先运行,然后 setTimeout 再 selectUser?

"selectUser" 将在 "addUser" 之后执行,但不会在 setTimeout 回调之后执行。如果要在 setTimeout 后运行“selectUser”,可以将其添加到 setTimeout 回调函数中。所以它应该看起来像:

 async function queryDB() {
  const addUser = await promisePool.execute("INSERT INTO Users (User) VALUES ('username')")

  setTimeout(async () => {
    const selectUser = await promisePool.execute("SELECT User FROM Users");
  }, 3000);
}

        

答案 3 :(得分:0)

重要说明:在这里可能是一个坏词。更好的说法是等待。更好的做法可能只是说使用 await 语法完成的操作保证按顺序执行。

大局

在 Node 中使用异步代码的主要原因是我们不会在等待一些异步操作(如数据库请求、网络请求、文件操作等)时阻塞整个应用程序。 - 我们只阻塞这个特定的执行上下文。

尽管 Node.js 只使用一个线程来执行用户代码,但 I/O 操作是异步的并且它们非阻塞

假设您有一个包含上面提供的代码的端点,该端点链接到前端的某个按钮“添加用户”。

  • 比尔按下按钮,你开始处理请求,开始等待addUser操作
  • 此时,John 也按下了相同的按钮。您的代码仍会被执行,并且会开始等待 addUser 完成。
  • 假设 Users 表陷入死锁,任何使用它的数据库操作都将非常缓慢,我们有第三个用户 Jane,她只是浏览您的网站,但她不需要登录/注册,这样她就不会碰Users 桌子。发生什么了?简完全没问题,她的请求会顺利通过。

如何等待 setTimeout

setTimeout 来自“以前的时代”,因此它使用回调而不是 async/await 语法。但是,如果需要,始终可以将回调样式转换为 async/await。

function delay(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}
await delay(5000);

答案 4 :(得分:0)

简而言之,await 只阻塞当前异步函数中 await 语句之后的代码,而不是整个线程。一旦 await 得到解决,其余的代码就会被执行。在您的示例中,由于您有 2 个 await 语句,因此在第一个完成之前,第二个不会运行,因为第一个阻塞了两个 await 语句所在的函数。对于第二个示例,由于 setTimeout() 不返回承诺并且您也不在等待它,因此该函数会将其视为“同步”操作。也就是说,它会将 setTimeout() 回调抛出到调用堆栈中,直到下一个事件循环并继续执行下一行,这是另一个 await 语句,该语句将再次阻塞该函数,直到它被解析为止。

为了一一执行所有三行,您必须将setTimeout()放入promise并等待它,以便JS将其视为异步操作并“等待”它在之前完成移至下一行。