【问题标题】:What makes a JavaScript function asynchronous?是什么让 JavaScript 函数异步?
【发布时间】:2018-10-07 14:22:42
【问题描述】:

我的目标是了解使它们异步的某些 JavaScript 函数究竟是什么。你可以有这样的功能:

function someRandomFunction () {
  for (let i of everything) {
    iterator++
    something = true
    hello = that + whatever
  }
}

这个函数没有什么是异步的。它做了很多事情,但速度很快。

然后采用这样的 Node.js 函数:

fs.readFile('path/to/file', (err, data) => {
  // do something
})

这个函数被声明为异步的。但这是为什么呢?背后的原因是什么?

是不是因为读取文件需要一定的时间才能完成,所以它是异步的?为什么在循环一些变量和做一些计算时是异步的不是异步的?

【问题讨论】:

  • 你有没有遇到过这个讨论函数的线程? stackoverflow.com/questions/10058814/get-data-from-fs-readfile
  • 所以,在不劫持下面很好的答案的情况下,我想补充一下:同步代码 = 我组装了一个披萨,把它放进烤箱,看着烤箱 20 分钟,披萨慢慢煮熟,然后吃了我的披萨。异步代码 = 我组装了比萨,把它放进烤箱,20 分钟去做别的事情,然后 20 分钟后比萨煮熟了。 Async 真的可以归结为“我不知道这段代码什么时候完成,我也没有直接的控制权。让我知道什么时候完成。”需要注意的是——代码可以很快。
  • Javascript 是单线程运行时,一次只能运行一件事。在 Node(和浏览器)中,异步操作被委托给事件循环,而同步操作则保留在堆栈上。看看这个 YouTube 视频,它可以帮助你了解发生了什么:Philip Roberts: What the heck is the event loop anyway?

标签: javascript node.js asynchronous


【解决方案1】:

异步的“想法”意味着“我们已经将控制权交给了其他一些操作代码,我们不知道其他代码何时允许我们再次操作”。

因此,在您的底部示例中,您可以控制节点的文件系统操作以读取文件。我们不知道该文件有多大,因此无法预测完成所需的时间。因此,JavaScript 允许我们提供一个“回调”,当异步操作完成时会触发它。当回调被执行时,控制权返回给我们的代码。

上面的例子是同步的,因为您的代码保持对操作的控制,而 Javascript 本质上是同步执行代码的。

【讨论】:

  • 你描述的听起来像协程。在 JS 中,对执行某些异步工作的函数的所有调用仍然会立即返回。
  • 当然,这就是我描述的 JS 的同步特性。我想为了不那么迂腐,我会冒犯知识渊博的人。对不起,伯吉。
  • @Bergi - 此外,我相当肯定 fs.readFile() 返回 undefined
  • 我喜欢这样的解释:“我们已经将控制权交给了其他一些操作代码,我们不知道其他代码何时允许我们再次操作”
【解决方案2】:

一个函数要么是显式同步的,要么是异步的;没有“一定的时间”可以自动使其异步。

使用 Node 时要记住的重要一点是它是“异步 I/O”,这意味着如果没有进程的输入/输出,就没有任何事情是异步的。您通常会在文件读取/写入、数据库调用以及任何需要网络调用的情况下看到这一点。

【讨论】:

    【解决方案3】:

    就 JavaScript 而言,同步函数和异步函数的区别在于代码的执行位置和时间。同步函数在当前调用堆栈中一个接一个地立即执行。异步函数被传递给事件循环,并且在当前调用堆栈完成执行后返回值。

    请注意,由于 JS 仅在单个进程线程上执行,因此 JavaScript 中没有什么是真正异步的

    【讨论】:

    • 异步函数被传递给事件循环,并且在当前调用堆栈完成执行后返回值。首先由谁返回..?
    • 事件回调 que 将其推回调用堆栈。此页面在可视化其工作方式方面做得很好:latentflip.com/loupe
    • 是的,但是结果究竟是如何产生的以及它的来源?事件队列只是一个观察者,用获得的数据而不是结果的来源调用回调。异步工作流抽象不仅仅是事件队列。
    • 什么决定了一个函数是否需要传递给事件循环?我认为这是我的问题的核心。
    • @JakeWilson 由编写程序的人决定。在您的示例中,“fs”可能会从一个大文件中读取,如果该函数是同步的,它最终会阻塞整个调用堆栈,直到文件被完全读取,这可能会导致最终用户出现明显的性能问题。这完全取决于用例和函数的估计运行时间。或者,您也可以强制循环异步
    【解决方案4】:

    这是一个基本但基本的问题。将一个函数传递给另一个函数不会使代码自动异步。这完全是关于尝试使用另一段代码,主要是未知来源,可能但不一定与操作系统的 IO 机制有关。换句话说,您通过将任务交给可能在单独线程上运行的已知或未知资源来要求在当前执行的 JS 上下文范围之外发生一些事情。

    正如我所说,这可能是一个 IO 操作或一个你不知道的数据库访问

    • 正在运行什么代码
    • 它在哪个线程运行
    • 需要多长时间
    • 会不会成功

    在幕后,这些外来代码最终触发事件队列,通过驱动程序调用回调函数。在像 Haskell 这样的纯函数式语言中,此类活动严格远离上下文,必须由单子类型函数处理。在 JS 中,这基本上是通过回调、promise 或 async-awiat 机制的异步工作流来完成的。

    一旦你把一个任务交给外部世界,它就没有办法干扰你的同步代码了。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-08-12
      • 1970-01-01
      • 2016-06-05
      • 1970-01-01
      • 1970-01-01
      • 2017-08-01
      • 2021-02-03
      相关资源
      最近更新 更多