【问题标题】:Asynchronous code, how does it work? Promises and callbacks异步代码,它是如何工作的?承诺和回调
【发布时间】:2018-02-27 22:43:37
【问题描述】:

我试图在网上找到答案,并部分做到了。但是我还是不能完全理解 JS 是怎么跑异步代码的?

我对事物的看法:

JS 赋予我们异步编程的能力。这意味着我们可以开始第一个任务,然后在一个正在进行时我们可以开始第二个任务,等等。 在 js 可以开始第二个任务之前,它应该从上一个任务中释放出来。它可以通过两种方式实现:

  • js自己结束任务(只需要js处理的代码)
  • js 可以启动例如应该由文件系统处理的任务。在这种情况下,js 完成它的工作,将任务传递给文件系统并开始处理其他排队的任务。当 js 被释放并且文件系统返回结果时,js 可以继续该任务。

所以我们不能仅仅通过编写下一个代码来实现异步:

function doSth( callback ) {
    let arr = [];

    for ( let i=1e6; i > 0; i-- )
        arr.push( i );

    callback();
}

doSth( console.log.bind( null, 'I am callback' );
console.log( 'just a line' );

由于 doSth() 只包含 js 的工作,它会先完成然后才出现'只是一行'?所以它不是异步的,对吧? 但是如果我们将文件系统任务插入循环,我们将有异步函数吗?

还有一个问题:promise 真的是异步的吗?它们怎么可能是异步的(我的意思是,其他任务可以在处理承诺时处理),或者承诺只是模仿异步代码? *我知道,还有额外的承诺队列。

也许我只是不明白一些基础?如果你能解释我的问题,让我的问题更明确,我会很高兴。

【问题讨论】:

标签: javascript asynchronous ecmascript-6 promise asynccallback


【解决方案1】:

我认为你说得对。

函数doSth 是同步的并且会阻塞主线程直到它完成。你只提供回调API,但这并不意味着它会神奇地变成异步的。

事实上,你编写的所有 JS 代码都是同步的,除非你使用定义为异步的核心 JS 函数(例如,fs.readFilesetTimeout$.ajax({...}).done)。没有它们,您将无法创建异步行为,您将不得不从头开始编写自己的 JS 核心,带有事件循环(实际上,我鼓励您谷歌并研究 javascript event loop 是什么,我相信它会澄清很多给你的东西,会让你更好地了解核心发生的事情)。那里的所有 3rd 方库都实现了异步行为,仅仅是因为它们使用这些核心函数并用自己的代码包装它们,从而提供更优雅和更高级别的 API。

同样适用于承诺。是的,它们是异步的,但前提是你用异步代码填充它们。实际上,它们有一些额外的开销并且不会立即运行代码,但是如果单个 promise 只包含同步代码,那么它的最终执行将阻塞主线程直到它完成。

【讨论】:

    【解决方案2】:

    Watch this playlist for a light overview of asynchronous Javascript.

    But if you really want to understand all the details, read this book.

    要回答您的主要问题之一,异步与文件系统无关。仍然是 Javascript。引用我推荐的书:

    异步是“当您的程序的一部分现在运行,而您的程序的另一部分稍后运行时——现在和以后您的程序没有主动执行之间存在间隙。”

    考虑你的代码:

    function doSth(callback) {
      let arr = [];
      for (let i=1e6; i > 0; i--){
        arr.push(i);
      }
      callback();
    }
    
    doSth(console.log.bind(null, 'I am callback'));
    console.log('just a line');
    

    这将输出

    我是回调

    然后

    就一行

    但是,如果您将代码更改为:

    function doSth(callback) {
      let arr = [];
      for (let i=1e6; i > 0; i--){
        arr.push(i);
      }
      setTimeout(callback, 0);
    }
    
    doSth(console.log.bind(null, 'I am callback'));
    console.log('just a line');
    

    你会得到

    就一行

    然后

    我是回调

    它本质上与调用回调的函数有关。 "doSth" 不是一个异步函数,无论 for 循环需要多长时间。然而,在第二个示例中,setTimeout 是调用回调的函数,而 setTimeout 是一个异步函数,它在您的同步代码的其余部分之后运行,因为这是它的预期工作方式。另一个常见的异步函数是任何类型的 AJAX 请求,并且提供给它的回调也将在任何其他同步代码之后调用。

    【讨论】:

    • 所以,异步就是:开始任务 -> 暂停任务 -> 继续任务?
    • 这是非常误导和模糊的。
    • 不,@VadimCherepenichev 更像是time slicing。 Javascript本质上是单线程的。所以它从不等待外部调用。它只是继续,然后您需要在一个单独的进程中处理来自外部调用的响应,一旦它返回,这将被安排在单个线程上运行。这一切都包含在我上面突出显示的重复项中
    • @Liam,是的,我知道这是单线程的。所以你的意思是 js 只是在函数的某个点停止。然后在队列的末尾(或设置计时器)添加新任务之类的其余操作,并在适当的时间后继续?
    • @Liam,我想我现在才明白我对异步的理解不正确。异步是否意味着简单地说:“嘿,js,使用这个函数,但你现在不应该对它做任何事情。但是当需要它时调用它”?
    猜你喜欢
    • 2018-12-02
    • 2016-01-08
    • 2019-08-29
    • 2014-09-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-01-15
    • 1970-01-01
    相关资源
    最近更新 更多