Event queues and event loops

We all know that JavaScript is a single-threaded, non-blocking scripting language, which means that JavaScript has only one main thread to handle all tasks during execution. Non-blocking means that when the code has an asynchronous task, the main thread will suspend the asynchronous task, and when the asynchronous task is finished, the main thread will execute the callback of the task at the appropriate time. When an asynchronous task is finished, JavaScript places the task in a queue, which we call an event queue. Callbacks on this queue are not executed immediately, but only when all tasks in the current execution stack have been processed. Queues are linear first-in, first-out tables. In the specific application commonly used linked list or array to achieve. Specific data reference wikipedia complete after the current execution stack, JavaScript has the task to check whether the current event queue, if there is a will this add to the current task execution stack performs this task, when after the completion of tasks executed and repeat this operation, form a loop, and the cycle we will call it the event loop. The overall process is shown in the figure below

Macro and micro tasks

Asynchronous tasks are divided into two types, microtask and macrotask. Different types of tasks will be assigned to different event queues and their execution timing will be different. For convenience, I call the microtask event queue microevent queue and the macrotask event queue macrotask queue. The event loop to check the current event queue, you check the current micro event queue for task, if there are added to the current execution stack, when after the completion of the inspection again, until the current after the event queue is empty, check again whether the current macro event queue tasks, if any, added to the current execution stack executed. The process is shown below

The code that executes for the first time is actually a macro task, so it’s the first time that the microevent queue is empty so it executes the task in the macro task queue. Tasks in the microevent queue are always executed before macro task queue tasks, except for the first load.

Microtask events include the following

  • Promise.then
  • MutaionObserver
  • Object. Observe (deprecated)
  • process.nextTick(NodeJS)

The events of a macro task include the following

  • setTimeout
  • setInterval
  • setImmediate
  • MessageChannel
  • requestAnimationFrame
  • I/O(reading files in web pages, or in NodeJS)
  • UI interaction events

thinking

Consider the order in which the following examples are printed on the console, and the answer will be posted at the end.

Example 1:

Micro / / task
const promise = Promise.resolve()
const microtask = (cb) = > {
  promise.then(cb)
}

/ / macro task
const macrotask = (cb) = > {
  setTimeout(cb)
}

macrotask(() = > {
  console.log('macrotask 1')
})

macrotask(() = > {
  console.log('macrotask 2')
})

microtask(() = > {
  console.log('microtask 1')})Copy the code

Example 2:

macrotask(() = > {
  microtask(() = > {
    console.log('microtask 2')})console.log('macrotask 1')
})

macrotask(() = > {
  console.log('macrotask 2')
  microtask(() = > {
    console.log('microtask 3')
  })
})

microtask(() = > {
  console.log('microtask 1')})Copy the code

Here are three examples of how THE vue2 rendering function works:

Example 3:

/** * html: * 
      
**/
const stack = [] const nextTick = cb= > microtask(cb) const render = fn= > { if(! stack.includes(fn)) { nextTick(() = > { const index = stack.indexOf(fn) if (index > -1) { stack.splice(index, 1) } fn() }) stack.push(fn) } } const observeItem = (obj, key, val, renderFn) = > { Object.defineProperty(obj, key, { set(newVal) { render(renderFn) val = newVal return true }, get() { return val } }) } const observe = (obj, renderFn) = > { for (let key in obj) { observeItem(obj, key, obj[key], renderFn) } renderFn() } const data = { count: 0 } const $container = document.querySelector('#output') observe( data, () = > $container.textContent = data.count.toString() ) nextTick(() = > { console.log($container.textContent) }) data.count = 100 Copy the code

Example 4:

data.count = 200
nextTick(() = > {
  console.log($container.textContent)
})
Copy the code

Example 5:

macrotask(() = > {
  console.log($container.textContent)
})
data.count = 300
Copy the code
































The answer

The answer is below, and I won’t explain it here, but remember that microtasks are always executed before macro tasks except for the first execution, event queues are always first in, first out, and it’s either the macro event queue or the microtask queue that joins first, so you can solve these problems.

1 / / examples
// "microtask 1"
// "macrotask 1"
// "macrotask 2"

2 / / examples
// "microtask 1"
// "macrotask 1"
// "microtask 2"
// "macrotask 2"
// "microtask 3"

3 / / examples
/ / "0"

4 / / examples
/ / "200"

5 / / examples
/ / "300"
Copy the code