I had a few interviews a few days ago, and every interview had a browser event loop, so I thought I'd write this article to summarize.Copy the code

Consider: setTimeout, promise, requestAnimationFrame execution order is mine?

What

First we need to understand what an event loop is:

  • In computer science, an event loop is a program construction or design pattern for waiting and scheduling events or messages in a program.
  • It works by making a request to one of the internal or external “event providers” (typically blocking the request until the event arrives) and then calling the relevant event handler (” scheduling the event “)
  • Event loops are sometimes called message dispatchers, message loops, message pumps, or run loops.
// Event loop framework
function main(a)messageTask = []
    while(messageTask.length ! = =0){
        task = messageTask.pop()
        execute(task)
        }
Copy the code

Why

Why do browsers use event loops? This is because the event loop is best suited to the work of the browser’s render thread, which executes the render event continuously at an interval of about 60 times per second, with the refresh interval basically sufficient for most ordinary tasks. In addition, the page will constantly add new tasks during operation, such as click events, XHR, setTimeout, etc., so it is necessary to constantly determine whether there are new tasks, and the event loop mechanism can solve this problem.

// Browser event loop
// Version 1 message queue
while(true){
    queue = getNextQueue()
    task = queue.pop()
    exexute(task)
    if(isRepaintTime()){
        repaint()
      }
}
Copy the code

How

It is well known that in addition to synchronous tasks, browsers also have Webapis like setTimeout and XHR, micro-tasks like Promise and MutationObserver, and requestAnimationFrame. How does the browser handle these asynchronous tasks?

webapis

A common setTimeout is to add a delay task directly to the delay queue and wait until a specified time before moving it to the message queue for execution. XMLHttpRequest, on the other hand, is executed by the browser’s web process, and the result is notified to the renderer via IPC, which then adds the corresponding message to the message queue.

Message queue tasks are called macro tasks, which have large time granularity and low implementation.

Micro tasks

A microtask is a function that needs to be executed asynchronously, after the completion of the main function but before the completion of the current macro task. It appears mainly to solve the problem of real-time task. There is no guarantee that the renderer will not put other tasks on the message queue while the main function is executing.

Imagine if you wanted to execute your callback method after the main function, using a macro task to put the callback method in a message queue?

// Browser event loop
// Version 2 message queue + microtask
while(true){
    queue = getNextQueue()
    task = queue.pop()
    exexute(task)
    // The microtask queue is emptied after each round of macro tasks
    while(microTaskQueue.hasTask()){
        doMicroTask()
    }
    
    if(isRepaintTime()){
        repaint()
      }
}
Copy the code

Tips: If a new microtask is created during the execution of a microtask, it will also be added to the microtask queue. This means that the new microtask created during the execution of a microtask will not be postponed to the next macro task, but will continue to be executed in the current macro task.

requestAnimationFrame

In modern browsers, the frame rate is 60 frames per second (16.7ms or so). The best time to update an animation is 16.7ms or so, hence the requestAnimationFrame. RequestAnimationFrame is executed at an interval consistent with the browser’s frame interval, which addresses setTimeout delays caused by too many message queue tasks.

// Browser event loop
// Version 2 message queue + microtask + requestAnimationFrame
while(true){
    queue = getNextQueue()
    task = queue.pop()
    exexute(task)
    
    while(microTaskQueue.hasTask()){
        doMicroTask()
    }
    
    if(isRepaintTime()){
        animationTasks = animationQueue.copy()
        for(task of animationTasks){
            doAnimation(task)
        }
        repaint()
      }
}
// The execution priority can be found from the code
//promise => requestAnimationFrame => setTimeout

Copy the code

Afterword.

Article length is not long, look at the code feeling better than read the text to understand. And the following videos are really great ~ highly recommended ~

reference

  • What the heck is the event loop anyway
  • Further Adventures of the Event Loop
  • wiki-Event loop