【问题标题】:Why are callbacks considered asynchronous if we need to wait如果我们需要等待,为什么回调被认为是异步的
【发布时间】:2016-12-31 01:24:00
【问题描述】:

我已经阅读了 Why do I have to use await for a method to run asynchronously. What if I don't want to wait for the method to finish before continuing?Are callbacks always asynchronous? 并且仍在尝试了解回调何时实际上是异步的。

例如,doThat 将需要等待 GET 数据才能对其进行任何操作。正如上面第二个链接所述,javascript 是单线程的。

doThis(doThat);

function doThis(callback) {
    $.get('http://some_api/some_resource', function (data) {
        callback(data);
    });
};

function doThat(data) {
    // Do something with data
};

我见过的唯一真正的异步功能是带有承诺和多个承诺,例如,我可以在动画结束时加载其他数据。我想帮助更好地理解传统回调何时实际上是异步的。具体例子有帮助。

【问题讨论】:

标签: javascript multithreading asynchronous


【解决方案1】:

从定义开始:

异步,在计算机编程中,是指事件的发生独立于主程序流程和处理此类事件的方式。这些可能是“外部”事件,例如信号的到达,或由程序发起的与程序执行同时发生的动作,而不会阻止程序等待结果。

-- 戴维斯,亚历克斯(2012 年)。 C# 5.0 中的异步,来自维基百科Asynchrony (computer programming)

在 JavaScript 的情况下,它的工作方式如下(简化):有一个任务队列等待由主(也是唯一的)线程执行。加载脚本时,它是放置在此队列中的任务。一个任务一直运行直到它退出,没有其他任务可以中断它。但是,一个任务可能会导致其他任务被放置在队列中。当一个任务完成时,队列中的下一个任务开始。如果队列为空,则之后进入队列的第一个任务立即执行。

任务进入队列的主要方式,除了是脚本被解析执行的主要任务外:触发事件会将为该事件注册的处理程序放入任务队列,并达到setTimeoutsetInterval 会将关联的任务放到任务队列中。

在 JavaScript 上下文中,在相同任务(“主程序流”)中执行的所有内容都被称为同步未来任务中执行的所有内容都称为异步。另一种判断方式是 - 如果下一条语句在回调之前执行,则它是异步的;如果下一条语句在回调之后执行,则它是同步的。

$.get(opts, callback)是异步调用,因为执行callback函数的任务在被onreadystatechange事件触发时会被放到任务队列中。如果后面有语句,它将首先执行;只有在该任务完成后,由于 AJAX 调用更新而进入任务队列的任务才有机会运行。

相比之下$.each(collection, callback)是同步调用,因为callback会直接从each函数中调用,不会退出当前任务,也不会产生任何额外的任务(反正不会自己产生,@987654330 @当然可以产生额外的任务)。下一条语句必须等到each 完成对collection 的每个元素的迭代。

请注意,promise 只是此机制的包装器。所有你可以用 Promise 做的事情,你也可以不用它们,只是代码不会那么漂亮和易读。

【讨论】:

    【解决方案2】:

    例如,我可以在动画结束时加载其他数据。

    这正是您可以对您的代码执行的操作。请注意,您可以在等待doThat 执行的同时运行其他代码(实际上“运行其他代码”会让人感到困惑)。和你的动画例子一模一样:

    function doThis(callback) {
        $.get('http://some_api/some_resource', function (data) {
            callback(data);
        });
    };
    
    function doThat(function (data) {
        // Do something with data
    });
    
    function doAnotherThing(function (data) {
        // Do something with data
    });
    
    doThis(doThat);
    
    // without waiting for doThat to run, you can IMMEDIATELY call:
    doThis(doAnotherThing);
    
    // without waiting for either doThat or doAnotherThing you
    // can output some pretty animations:
    setInterval(do_animation,100);
    
    // and without waiting for any of the above you can do anything else:
    document.body.innerHTML += 'hello';
    

    请注意,让大多数人感到困惑的是document.body.innerHTML='hello' 部分在其上面的任何代码之前运行。这意味着回调是异步的,因为它们在数据到达时被执行,而不是在函数被调用时。

    请注意,并非所有回调都是异步的。例如,Array.prototype.forEach() 是同步的:

    document.body.innerHTML += 'before<br>';
    ['hello','world'].forEach(function(x){
        document.body.innerHTML += x;
    })
    document.body.innerHTML += '<br>after';
    

    决定回调是异步还是同步的是调用它的函数。另见:I know that callback function runs asynchronously, but why?

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-09-11
      • 2020-09-12
      • 2019-10-11
      • 2022-01-16
      • 2020-03-10
      相关资源
      最近更新 更多