网页异步和非异步
JavaScript’s special concurrency model makes it suitable for a variety of purposes. Asynchronous programming is arguably node.js’ greatest strength. That is also the reason behind widespread popularity of node.js. Let us take a quick trip to the mystical land of JavaScript and try to understand how it handles asynchronous execution under the hood.
JavaScript的特殊并发模型使其适用于多种用途。 异步编程可以说是node.js的最大优势。 这也是node.js广泛普及背后的原因。 让我们快速浏览一下JavaScript的神秘领域,并尝试了解它如何在后台处理异步执行。
Synchronous execution is when statements are simply executed one after the other. Asynchronous execution is when statements don’t have to execute in that rigid order. They can be executed in any order. That is often used to perform multiple tasks simultaneously.
同步执行是指简单地一个接一个地执行语句。 异步执行是指语句不必按严格的顺序执行。 它们可以以任何顺序执行。 通常用于同时执行多个任务。
Consider the following analogy. You are doing your household chores. That includes doing the dishes, laundry and cooking your lunch. You can do all those tasks one by one (synchronously). But that isn’t very efficient, is it? So you do them all at once. You pile the dishes and start up the dishwasher. While that happens, you collect the laundry and put it in the washing machine. While it works its magic, you start chopping veggies for lunch. You keep alternating between tasks till all of them are done.
考虑以下类比。 您正在做家务。 这包括洗碗,洗衣服和做午餐。 您可以一一(同步)完成所有这些任务。 但这不是很有效,是吗? 因此,您一次完成所有操作。 您将碗堆起来,然后启动洗碗机。 在这种情况下,您收集衣物并将其放入洗衣机。 当它发挥神奇的作用时,您便开始将蔬菜切碎作为午餐。 您可以在任务之间交替切换,直到所有任务都完成为止。
Let us understand how JavaScript handles asynchronous code. Most of the languages use a multi-threaded model with pre-emption. JavaScript on the other hand is single threaded by specification. If you don’t understand what that means, a quick google search can be useful. You can also choose to ignore this, as it isn’t really relevant to our discussion.
让我们了解JavaScript如何处理异步代码。 大多数语言使用带抢占的多线程模型。 另一方面,JavaScript按规范是单线程的。 如果您不明白这是什么意思,可以使用Google快速搜索。 您也可以选择忽略这一点,因为它与我们的讨论并不真正相关。
JavaScript can do only one thing at a time. That creates a problem when you want to perform operations additional to the main computation. For example:
JavaScript一次只能做一件事。 当您要执行除主要计算之外的其他操作时,这会产生问题。 例如:
… perform computationset timer : After 1000ms, print — “Timer Done”… perform computationTo process the timer, JS engine has to stop performing computation and wait for 1000ms. When the timer completes, it prints “Timer Done”. After that, it can resume computation. This is not very efficient. Fortunately, there is a way around
要处理计时器,JS引擎必须停止执行计算并等待1000ms 。 计时器完成后,将显示“ Timer Done”。 之后,它可以恢复计算。 这不是很有效。 幸运的是,有办法
When JS engine wants to perform additional operations, it simply hands off the work to its environment. The environment does the work and notifies the JS engine. JS engine then takes appropriate action. This is asynchronous execution. Let us take a few example to make things clear.
当JS引擎想要执行其他操作时,它只是将工作移交给其环境。 环境完成工作并通知JS引擎。 然后,JS引擎将采取适当的措施。 这是异步执行。 让我们举几个例子来说明清楚。
You make an AJAX request for data fetching. Your code schedules network I/O with browser. Browser (the environment) makes the network call and waits for it to finish. When the network call finishes, JS engine is notified. 您发出AJAX请求以获取数据。 您的代码通过浏览器计划网络I / O。 浏览器(环境)进行网络呼叫并等待其完成。 网络通话结束后,将通知JS引擎。 You make a SQL query in node.js. Your code tells the OS to connect to the SQL server and make the request. When the OS completes that, it notifies the node.js engine. 您在node.js中进行SQL查询。 您的代码告诉操作系统连接到SQL Server并发出请求。 操作系统完成该操作后,它会通知node.js引擎。 You set a timer. The environment (Browser or OS) starts a timer. When the timer runs out, engine is notified. 您设置了一个计时器。 环境(浏览器或操作系统)启动计时器。 当计时器用完时,将通知引擎。 You want to listen for click on a button. You schedule a listener with the Browser. Whenever user clicks on the button, engine is notified. 您想听一个按钮的点击。 您可以使用浏览器安排侦听器。 每当用户单击按钮时,就会通知引擎。JS engine executes code using event loop. Let us try to understand how that works. There are a few terms you should be familiar with:
JS引擎使用事件循环执行代码。 让我们尝试了解它是如何工作的。 您应该熟悉以下几个术语:
Task: A chunk/piece of code that is to be executed.
任务:要执行的代码块/一段。
Task queue: A queue that keeps track of the tasks that are to be executed.
任务队列:跟踪要执行的任务的队列。
Event loop: A loop that iterates over the task queue and processes the tasks.
事件循环:循环访问任务队列并处理任务的循环。
Note that tasks dictate what to do. The script that you write for JS engine to execute is called the main task. Task queue is initialised with the main task. When you schedule an operation with the environment, you need to provide a task. That is usually in the form of callback functions. When the operation completes, environment pushes the appropriate task to the task queue.
请注意,任务决定了要做什么。 您为JS引擎编写的要执行的脚本称为主要任务。 任务队列由主任务初始化。 在环境中安排操作时,需要提供任务。 通常以回调函数的形式。 操作完成后,环境会将适当的任务推送到任务队列。
Below is a high level algorithm for how event loop works:
以下是事件循环如何工作的高级算法:
taskQueue.enqueue(main task)while(there are tasks to process) size = taskQueue.size() while(size--) dequeue the task and executeLet us see it step by step:
让我们一步一步看它:
The loop will execute till there are tasks to process. This means that either the task queue is not empty. Or it is empty but an operation has been scheduled with the environment. In that case, a task will come to the queue in the future. Event loop will wait for the task. 循环将一直执行到有任务要处理为止。 这意味着任务队列不为空。 或为空,但已根据环境安排了操作。 在这种情况下,将来会有一个任务进入队列。 事件循环将等待任务。 Before beginning the iteration, event loop locks the size of the queue. If there are any new tasks added while the loop processes the queue, they are considered in the next iteration. 在开始迭代之前,事件循环将锁定队列的大小。 如果在循环处理队列时添加了任何新任务,则将在下一次迭代中考虑它们。 Event loop processes the tasks in FIFO manner — first in first out. 事件循环以FIFO方式处理任务-先进先出。 Melanie Pongratz on Melanie Pongratz在 Unsplash Unsplash上Let us finish with an example:
让我们以一个示例结束:
console.log(1)setTimeout(() => { console.log(2) setTimeout(() => { console.log(3) }, 0)}, 0)Output:
输出:
123In the first iteration, main task is processed. That prints 1. It schedules a timer for 0ms. First iteration is completed.
在第一次迭代中,主要任务被处理。 打印1 。 它计划一个定时器为0ms 。 第一次迭代完成。
Timer is ready. Loop begins second iteration. It takes the task (the timer callback) out of the queue and runs it. This prints 2. This task again schedules a timer for 0ms. Second iteration is completed.
计时器已准备就绪。 循环开始第二次迭代。 它将任务(计时器回调)从队列中取出并运行它。 打印2 。 此任务再次将计时器安排为0ms 。 第二次迭代完成。
Timer is ready. Loop begins third iteration. It takes the task and runs. This prints 3.
计时器已准备就绪。 循环开始第三次迭代。 它接受任务并运行。 打印3 。
Task queue is empty. There is nothing scheduled with the environment. Therefore, all processing is over. Event loop terminates. 任务队列为空。 没有安排任何环境安排。 因此,所有处理都结束了。 事件循环终止。Still confused? Check out this awesome visualisation.
还是很困惑? 看看这个很棒的可视化。
ES6 introduced the concept of microtasks. They help us manage and modify the flow of our asynchronous execution. Consider below snippet:
ES6引入了微任务的概念。 它们帮助我们管理和修改异步执行流程。 考虑以下代码段:
setTimeout(() => { console.log(1) setTimeout(() => console.log(2))})setTimeout(() => { console.log(3) setTimeout(() => console.log(4))})Output:
输出:
The first iteration — main task schedules two timers (1 and 3). Second iteration again schedules two timers (2 from 1 and 4 from 3). There are 3 iterations in total.
第一次迭代-主要任务安排了两个计时器(1和3)。 第二次迭代再次调度两个计时器(1中的2个计时器和3中的4个计时器)。 总共有3次迭代。
1 (second iteration)3 (second iteration)2 (third iteration)4 (third iteration)During the second iteration, you schedule 2 and 4 for the third iteration. 4 is executed after 2 due to FIFO nature. But what if we need to execute 4 before 2? What do we do in that case?
在第二次迭代中,您为第三次迭代计划2和4。 由于FIFO的性质,在2之后执行4。 但是,如果我们需要在2之前执行4,该怎么办? 在那种情况下我们该怎么办?
With ES6 specs, event loop can be written as following:
使用ES6规范,事件循环可以编写如下:
taskQueue.enqueue(main task)microtaskQueue = []while(there are tasks to process) size = taskQueue.size() while(size--) dequeue the task and execute while(microtask queue is not empty) process microtasksWe have a new queue called microtask queue. This is similar to task queue. Chunk of code is added to it is called a microtask. At the end of every iteration of event loop, microtask queue is processed. Note that this queue is not locked. So if any new microtasks are added to the microtask queue, they are processed in the same iteration and not the next one. Any high priority computation can be processed using microtasks.
我们有一个称为微任务队列的新队列。 这类似于任务队列。 将代码块添加到其中称为微任务。 在事件循环的每次迭代结束时,都会处理微任务队列。 请注意,此队列未锁定。 因此,如果将任何新的微任务添加到微任务队列,它们将在同一迭代中处理,而不是下一个迭代。 可以使用微任务处理任何高优先级的计算。
To schedule a microtask, simply use queueMicrotask function. Our beloved Promises use microtasks under the hood. A subtle but important detail. Anything scheduled using a promise is processed by microtask queue.
要计划微任务,只需使用queueMicrotask函数。 我们心爱的Promises在后台使用微任务。 一个细微但重要的细节。 使用promise计划的任何内容均由微任务队列处理。
Now let use revisit the example:
现在,让我们重新使用该示例:
setTimeout(() => { console.log(1) setTimeout(() => console.log(2))})setTimeout(() => { console.log(3) queueMicrotask(() => { console.log(4) queueMicrotask(() => console.log(5)) })})Output:
输出:
1 (Second iteration)3 (Second iteration)4 (Second iteration - at the end)5 (Second iteration - at the end)2 (Third iteration)This is a general idea of how a JS engine handles concurrency. It is enough to be able to understand and write correct and performant async code. If you wish to get into specifics of the event loop, node.js’ implementation is a good place to start.
这是JS引擎如何处理并发性的一般思路。 足以理解和编写正确的高性能异步代码就足够了。 如果您想了解事件循环的细节, node.js的实现是一个不错的起点。
If you notice any corrections or have any feedbacks, please leave them in the comments below ; )
如果您发现任何更正或有任何反馈,请留在下面的评论中; )
翻译自: https://medium.com/swlh/the-asynchronous-circus-65f42b0be6d
网页异步和非异步
相关资源:四史答题软件安装包exe