【问题标题】:Are javascript's async functions actually synchronous?javascript 的异步函数实际上是同步的吗?
【发布时间】:2020-01-14 06:56:58
【问题描述】:

我试图弄清楚异步代码在 Javascript 中是如何工作的。现在,我了解到JS中实际上有一个单线程执行队列中的作业,并且只有在当前作业完成后才能开始执行下一个作业(即如果所有同步代码或异步函数都已完成) .

现在,令人困惑的部分是实际上算作异步函数的部分 - 哪些实际被放入队列中的单独作业中,哪些没有。

首先,我们有函数的async 关键字。那么这是否意味着这些功能将被放入队列中的单独作业中并在将来的某个地方执行?好吧,实际上答案是NO。但请耐心等待,我将解释。

据我了解,理论上,JS 线程应该首先执行所有同步代码,直到它完成,同时延迟所有异步函数、承诺和回调的执行,将它们作为作业放置到末尾队列。然后,一旦所有同步代码完成,它将开始执行所有那些堆积起来的工作。

所以如果我有以下代码:

async function asyncFunc() {
    console.log("executing async function");
}

console.log("starting sync code");
asyncFunc().then(() => {
    console.log("executing callback of async function")
});
console.log("sync code completed");

那么理论上应该先执行所有同步代码,然后才开始执行异步函数,然后是回调:

starting sync code
sync code completed
executing async function
executing callback of async function

但实际情况不同!实际上,它实际上同步地 执行异步函数,以及其余的同步代码。唯一真正放入作业队列的是异步函数的回调

starting sync code
executing async function
sync code completed
executing callback of async function

那是什么意思? async 函数实际上是一个谎言?看起来是这样,因为它们实际上是 正常的同步函数,您可以碰巧将 async 回调附加到它们。

现在,我知道async 实际上是返回Promise 的函数的语法糖,例如:

async function asyncFunc() {
    console.log("executing async function");
}

是语法糖:

function asyncFunc() {
    return new Promise((resolve) => {
        console.log("executing async function");
        resolve();
    });
}

但我的观点仍然存在。您传递给 Promise 的所谓异步函数实际上是同步执行的。好吧,从技术上讲,Promise 对象并不意味着它将异步执行,但 async 关键字却可以!所以这是彻头彻尾的虚假信息,它让你相信它是异步的,而事实证明并非如此。

【问题讨论】:

  • 你的 asyncFunc 实际上是 function asyncFunc() { console.log("executing async function"); return new Promise(); } 的同义词 所以没有谎言 - 它只是没有按照你的想法做。
  • 同步部分和异步功能“并行”运行。您可以尝试添加延迟,然后异步输出将再次在同步输出结束后。
  • 如果你编写了一个从不调用awaitasync 函数,那么是的,这并不奇怪。 async/await 语法实际上就是这样:使用纯 Promise 对象管理 .then() 回调代码链问题的语法便利。
  • 您误解了async/await 语法的意图。
  • 真正将async 视为一种“类型”声明的最佳方式,它告诉JavaScript“此函数返回一个或多个Promise 对象”。然后解析器可以将该函数的代码构建为 generator 函数,每个内部 await 表达式实际上意味着另一个 Promise 的 yield

标签: javascript node.js asynchronous async-await


【解决方案1】:

这里的解释很清楚,没有混淆:https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Asynchronous/Async_await

有关说明,请参阅上述链接中的“处理异步/等待减速”部分。

【讨论】:

    【解决方案2】:

    只是为了证明我和@CertainPerformance 的观点。 这是相同的示例,其中的睡眠函数在等待解析之前等待 500 毫秒:

    async function sleep(msec) {
      return new Promise(resolve => setTimeout(resolve, msec));
    }
    
    async function asyncFunc() {
      await sleep(500);
      console.log("executing async function");
    }
    
    console.log("starting sync code");
    asyncFunc().then(() => {
      console.log("executing callback of async function")
    });
    console.log("sync code completed");

    这里只有一个 console.log:

    async function sleep(msec) {
      console.log('test');
    } 
    
    async function asyncFunc() {
      await sleep(500);
      console.log("executing async function");
    }
    
    console.log("starting sync code");
    asyncFunc().then(() => {
      console.log("executing callback of async function")
    });
    console.log("sync code completed");

    【讨论】:

    • 是的,这很有效,因为setTimeout 总是安排一个新工作。这似乎是强制代码异步执行的唯一方法。但是,例如,如果您将 setTimeout(resolve, msec) 替换为 console.log,它仍然会同步调用它。
    • 我的意思是console.log本身会被同步调用。我现在看到只有带有 await 关键字的行之后的代码实际上是异步调用的。
    【解决方案3】:

    就像构造 Promise 时一样,async 函数中的任何同步操作在遇到任何 awaits 之前都会同步执行。 async 函数只会在遇到 await 时停止执行其代码 - 在那之前,它也可能是一个普通的非 async 函数(除了它将返回值包装在承诺)。

    async function asyncFunc2() {
      console.log("in Async function 2");
    }
    async function asyncFunc1() {
      console.log("in Async function 1");
      await asyncFunc2();
      console.log('After an await');
    }
    console.log("starting sync code");
    asyncFunc1().then(() => {
      console.log("Received answer from async code");
    });
    console.log("finishing sync code");

    正如您在上面的 sn-p 中看到的,主线程仅在 asyncFunc1await 之外恢复一次 asyncFunc1(以及由该 @987654330 调用的所有 同步代码@) 已完成。

    async 是一个关键字,它允许您在函数内部使用await,但它本质上 没有任何其他含义,真的 - 它只是一个关键字。函数可能甚至可以同步运行它的所有代码(虽然这看起来有点奇怪)。

    【讨论】:

    • 其实这似乎也是错误的。它也会在await 之后同步执行代码。示例:javascript async function asyncFunc2() { console.log("in Async func 2"); } async function asyncFunc1() { console.log("in Async func 1"); await asyncFunc2(); } console.log("starting sync code"); asyncFunc1().then(() => { console.log("Received answer from async code"); }); console.log("finishing sync code"); 输出:javascript starting sync code in Async func 1 in Async func 2 finishing sync code Received answer from async code
    • 你没有等到 after 一个 await 来记录事情,所以你在最后看到 finishing sync code,就在 Promise 解决和 @987654338 之前@ - 你的函数也可以是return Promise.resolve()。只有在 await 之后完成某些事情时,您才会看到在较低同步代码完成后完成这些事情
    • 是的,所以这使它们不是真正的“异步”函数,而是“仅在行后异步关键字,如果它存在”的函数。
    • @YevgeniN 我认为您应该更多地了解async/await 的实际含义。第一个等待是return new Promise ... 部分的位置
    • @YevgeniN 是的,这是一种非常精确的表达方式。只有在所有同步较低代码完成后,您才能放置 Promise.resolve().then(asyncFunc1) 来运行东西
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-02-10
    • 2017-08-16
    • 2019-07-04
    • 2012-02-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多