在异步JavaScript程序中,函数调用的顺序如何? [英] How exactly are the function calls ordered in an asynchronous JavaScript program?

查看:55
本文介绍了在异步JavaScript程序中,函数调用的顺序如何?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在学习JavaScript(JS)中的异步编程的概念.但是,我很难理解这一点.在过去的几天里,我一直在互联网上阅读各种文章以了解它,但是我无法理解这个想法.

I am learning the concept of asynchronous programming in JavaScript (JS). But, I am having a hard time understanding the same. For the last few days, I had been reading various articles on the internet to understand it, but I am unable to grasp the idea.

所以,这是我的疑问:

setTimeout(function(){ alert("Hello 1"); }, 3000); // .....(i)
console.log("Hi!");                              // .....(ii)
setTimeout(function(){ alert("Hello 2"); }, 2000); // .....(iii)

  1. 考虑上面的代码.我了解到JS使用了一个调用栈和一个事件队列来命令执行.在上面的代码中,当JS解释器看到(i)行时,它将把 setTimeout 排入事件队列,然后移至(ii),将其放入调用堆栈,执行它,然后移至(iii),再次将 setTimeout 排入事件队列(此队列为非空),对吗?

  1. Consider the above code. I learnt that JS uses a call-stack and an event-queue to order the execution of instructions. In the above code, when the JS interpreter sees the (i) line, it will enqueue that setTimeout into the event-queue, then moves to (ii), puts it in the call-stack, executes it, then moves to (iii), where it again enqueues the setTimeout into the event-queue (and this queue is not empty), right?

如果我在上述问题中写的是正确的,则由于调用栈为空,一旦代码到达末尾, setTimeout 就会进入事件-队列一一执行,对吗?-这意味着如果我们假设(说) 10ms 到达代码的末尾,那么由于事件队列的前面有 setTimeout(i),它等待3秒,然后弹出警报:"Hello 1",在 time = 3010ms 时,将其出队,以及类似的 setTimeout(iii)在另外2秒钟后被执行,然后发出警报:"Hello 2"在 time = 5010ms 弹出,对吗?

If what I had written in the above question is correct, then once we get to the end of the code since the call-stack is empty the setTimeouts enqueued into the event-queue get executed one by one, right? - That means if we assume it took (say) 10ms to come to the end of the code, then since the event-queue has the setTimeout (i) in the front, it waits for 3s, then pops the alert: "Hello 1", at the time = 3010ms, the dequeues it, and similarly the setTimeout (iii) gets executed after 2 more seconds and then the alert: "Hello 2" pops at the time = 5010ms, right?

让我们假设我们使用 addEventListener()的而不是(i)和(iii)的 setTimeout ,并带有一些回调函数.即使在这种情况下,事件侦听器的回调函数是否也可以放入事件队列中?我觉得他们不会入队,因为我们可以在(i)的回叫之前触发(iii)的回叫.那么,在这种情况下究竟会发生什么呢?除了调用堆栈和事件队列之外,还有其他什么可以存储有关它们的信息并相应地触发它们的回叫吗?

Let's suppose that instead of setTimeouts at (i) and (iii), we had addEventListener()'s with some call-back functions. Even in this case, will the call-back functions of the event listeners be enqueued in the event-queue? I feel they don't get enqueued because we could have triggered the call-back of (iii), before the call-back of (i). So, what exactly happens in this case? Is there anything else other than the call-stack and event-queue that somehow stores the information about them and triggers their call-backs accordingly?

简而言之,这些指令是如何准确订购的?到底发生了什么?

In a nut-shell how exactly are the instructions ordered? What exactly happens in the background?

我将非常感谢您提供全面的答案.如果您还可以提供与此主题有关的一些综合材料的链接.

I would be really thankful for a comprehensive answer. It would be great if you can also provide links to some comprehensive materials on this topic.

谢谢您的帮助!

推荐答案

您可能已经知道,JavaScript引擎现在在单个线程上执行,那么异步操作如何处理?在下面的语句中,您部分是正确的,但还有更多内容:

As you might be aware by now JavaScript engine executes on a single thread, so how are asynchronous operations handled? You are partially true in the below statement, but there is more to it :

考虑上面的代码.我了解到JS使用了一个调用栈和一个事件队列来命令执行指令.

Consider the above code. I learnt that JS uses a call-stack and an event-queue to order the execution of instructions.

是的,我们确实有一个调用堆栈和一个事件循环.但是我们还有一个 WEB API环境回叫队列和一个微任务队列.

True, we do have a call stack and an event loop. But we also have a WEB APIs environment, Call-back Queue and a Micro-task Queue.

每当有任何异步任务时,它就会移至WEB API环境,例如,当您在"src"标签中有一个带有非常大图像的标签时.属性,此图像不会同步下载,因为这会阻塞线程,而是将其移到加载该图像的WEB API环境中.

Whenever there is any asynchronous task, it moves to the WEB API Environment, for example, when you have an tag with a very large image in the "src" attribute, this image is not downloaded synchronously, because that would block the thread, instead it is moved into the WEB API Environment where the image is loaded.

<img src="largeimg.jpg">

现在,如果要在加载图像后执行某项操作,则需要收听图像的"加载"事件.

Now, if you want to do something once the image is loaded, you will need to listen to the image's 'load' event.

document.querySelector('img').addEventListener('load', imgLoadCallback);

现在,一旦加载了图片,该回调函数仍不会执行,现在,它已被移入回调队列.回调函数在回调队列中等待,事件循环将检查同步代码,并等待直到调用堆栈为空.一旦调用堆栈为空,事件循环将在一个事件循环刻度中将第一个回调函数推入调用堆栈.那就是执行该回调函数的时间.

Now once the image has been loaded, this callback function is still not executed, instead now it is moved into the callback queue. The callback function waits in the callback queue, the event loop will check for synchronous code, and wait until the call stack is empty. Once the call stack is empty, the event loop will push in a first in callback function into the call stack in one event loop tick. And that is when that call back function is executed.

但是,当存在诸如Promises之类的微任务时,情况会发生变化.有了承诺后,它将被发送到微任务队列.微任务将始终具有优先于回调的优先级,并且它们可以并且将暂停回调,直到执行它们为止,事件循环将始终对微任务进行优先级设置.

However, this changes when there are micro-tasks such as Promises. When there is a promise, it is sent to the microtask queue. Microtasks will always have priority over the callbacks and they can and will halt the callbacks until they are executed, event loop will always prioritize microtasks.

这是JavaScript调用堆栈,事件循环,回调队列,微任务队列和WEB API环境的工作方式.

现在运行下面的代码,然后运行,然后尝试猜测结果.这将完全按照我在上面写的内容:

Now Run this below code, before running try to guess the outcome. It will be exactly as per what I have written above :

//Synchronous Code - Always prioritized over async code
console.log('Asynchronous TEST start');

//It is a 0 Second Timer, But a timer is not a microtask
setTimeout(() => console.log('0 sec timer'), 0);

//Promise is a microtask
Promise.resolve('Resolved promise 1').then(res => console.log(res)); 

//2nd promise is a microtask too
Promise.resolve('Resolved promise 2').then(res => {
  for (let i = 0; i < 1000000000; i++) {} //very large loop
  console.log(res);
});

//Synchronous Code - Always prioritized over async code
console.log('Test end');

以上代码片段的"SPOILER ALERT":

如您所见,计时器虽然最后是0秒计时器,但最后还是运行,但实际上并没有在0秒时执行.这是为什么?由于Settimeout使用回调,并且Promise是微任务,因此Microtask Priority始终大于Callback Priority

As you can see, the timer runs in the end although it is a 0 second timer, it does not actually execute at 0 seconds. Why is that? Because Settimeout uses a callback, and promises are microtasks, Microtask Priority is always greater than Callback Priority

这篇关于在异步JavaScript程序中,函数调用的顺序如何?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆