涉及异步调用时如何设置具体的执行顺序?

How do I set a specific order of execution when asynchronous calls are involved?

我是 JavaScript 世界的新手(2 天!!),我之前唯一的编码经验是在 Java 中按顺序执行语句。 我明白或者至少我读过 JavaScript 是异步的,这意味着如果有一条语句需要很长时间才能执行,那么下一条语句将在不暂停第一个语句的程序的情况下执行。 我遇到过回调(实际上很多!!),但我看不出如何使用它们来确定执行顺序。我写了一段代码只是为了了解它是如何完成的,我肯定需要一些帮助。

console.log("Beginning");

function Test(callback){
   setTimeout(function(callback){
       console.log("Something that takes a lot of time");
   },5000);
   callback();
}

function tstCallBack(){
    console.log("Should come last");
}

Test(tstCallBack);

我想要的是输出显示 -

Beginning
Something that takes a lot of time
Should come last

但我得到的输出是 -

Beginning
Should come last
Something that takes a lot of time

我能做些什么来按照我想要的方式获得输出吗?

将回调放在 setTimeout 内部而不是外部,因为 callback 将在 setTimeout 执行之前首先执行,因为 javascript 不会等待 setTimeout 执行( as JS is synchronous by nature) 并执行下一行,因此您不会获得所需的输出。

console.log("Beginning");
function Test(callback){
   setTimeout(function(){
    console.log("Something that takes a lot of time");
    callback();
   },5000);
 }
function tstCallBack(){
   console.log("Should come last");
}
Test(tstCallBack);

Demo

你说的很多都是错的。 Java脚本与Java一样是顺序的,但异步调用的频率更高。如果你想让你的回调在长的东西之后被调用,你必须在长的运行程序之后调用它。像这样 -

console.log("Beginning");
function Test(callback){
   setTimeout(function(callback){
    console.log("Something that takes a lot of time");
    callback();
   },5000);

 }
function tstCallBack(){
   console.log("Should come last");
}
Test(tstCallBack);

我已如下修改您的代码以获得所需的输出。

console.log("Beginning");
function Test(callback){

    console.log("Something that takes a lot of time");
    setTimeout(callback,5000);
 }

function tstCallBack(){
   console.log("Should come last");
}
Test(tstCallBack);

setTimeout 接受一个回调函数,将在指定的时间间隔后执行

setTimeout的使用是异步部分。上面代码执行时,首先打印"Begining"控制台语句,然后调用Test函数,传入一个需要在500ms后异步执行的函数。

让我们澄清一下您所说的内容:

I am new(2 days!!) to the world of JavaScript and my only prior coding experience is in Java where execution of statements takes place sequentially. I understand that or at least I've read that JavaScript is asynchronous which means that if there is a statement that takes a long time to execute, the next statement is executed without holding up the program for the first statement.

这不是它的工作原理。给定的函数在设计上要么是异步的,要么是同步的。它与执行所需的时间完全无关。您可以有一个非常快的异步函数或一个非常长的同步函数。决定函数是否异步的是它是如何设计的。如果它使用异步 I/O 或定时器或任何其他异步基础设施,那么至少部分函数的执行是异步的。这意味着一些函数将在稍后完成,而在此函数调用之后的一些代码将在异步部分完成之前执行。

I came across callbacks(a lot actually!!) but I couldn't see how they could be used to determine the order of execution. I wrote a piece of code just to understand how it could be done and I sure could use some help.

回调用于在某些异步操作完成时通知调用代码。这可用于消耗异步操作的结果,也可用于在异步操作完成后按顺序执行下一段想要 运行 的代码。

在您的代码示例中,如果您想要所需的序列,则必须在 setTimeout() 回调中调用回调,以便在调用的 setTimeout() 执行后调用它,从而为您提供所需的顺序。

您还必须删除 setTimeout 回调的 callback 参数。该回调没有通过该参数传递,因此声明它是错误的。它可以通过闭包直接从父函数访问,如下所示:

console.log("Beginning");

function Test(callback){
   setTimeout(function(){
       console.log("Something that is asynchronous");
       // call the callback here to indicate to the calling code
       // that the asynchronous operation is now complete
       callback();
   },5000);
   console.log("After Setting Timer");
}

function tstCallBack(){
    console.log("Should come last");
}

Test(tstCallBack);

这将在控制台中生成一个序列:

Beginning

After Setting Timer

Something that is asynchronous

Should come last


从概念上讲,Javascript 引擎 运行 是一个单线程,并且该单线程使用事件队列。所以,在你上面的函数中,这就是发生的事情。

  1. 执行第一个console.log("Beginning");
  2. Test(tstCallback) 被调用。
  3. 作为执行 Test() 函数的一部分,会安排一个计时器。这会在 JS 引擎内部注册一个计时器。
  4. Test() 中的代码继续执行,console.log("After Setting Timer"); 执行,然后该函数完成。
  5. 当前JS线程执行完毕,如果事件队列中没有其他内容,则JS引擎无事可做,等待下一个事件发生。
  6. 一段时间后(您的计时器设置的 5 秒),内部计时器触发并将计时器事件放入 JS 事件队列中。
  7. 由于此时没有其他JS执行,定时器事件被拉出事件队列执行。这意味着调用了为计时器注册的原始回调。
  8. 调用计时器回调时,它会执行 console.log("Something that is asynchronous"); 行,然后调用 callback().
  9. 然后调用您的 tstCallback 函数并执行 console.log("Should come last");
  10. 异步事件完成执行,JS 引擎查看事件队列中是否还有更多事件。如果是,则下一个事件从队列中拉出,它是 运行.

有很多关于 Javascript 如何处理异步操作的非常好的参考资料:

How does JavaScript handle AJAX responses in the background?

How Javascript Timers Work

Do I need to be concerned with race conditions with asynchronous Javascript?