The HTML specification is extremely long and explains many of the low-level details of the front-end technology. I don’t have the ambition to translate the whole thing. I’m just going to pick a few things that interest me. If there is any inappropriate content in the translation, you are welcome to put forward your valuable opinions.

3. Event processing model

As long as the event loop exists, the following steps are repeated:

  1. Select one of the task queues in the event loop as the taskQueue in a manner defined by the implementer, but the selected taskQueue must contain at least one executable task. If there is no such task queue, the following microtask steps are performed instead.

The way taskQueue is selected depends on how the implementer implements the user agent.

Note that the microtask queue is not a task queue, so it is not selected in this step. However, task queues associated with microtask sources may be selected. In this case, the next selected task is initially a microtask, but is removed as part of the spin event loop.

  1. The first executable task in the taskQueue is assigned to oldestTask, which is then removed from the taskQueue.
  2. Assign the currently executing task of the event loop to oldestTask.
  3. Assign the current time precision to taskStartTime.
  4. Run steps in oldestTask.
  5. Reset the currently executing task of the event loop to NULL.
  6. Microtasks: Run microtasks to perform checks.
  7. Set hasARenderingOpportunity to false.
  8. Assigns the current time precision to now.
  9. Perform the following steps to report task execution time:
    1. Sets the top-level browse context set to an empty collection.
    2. Iterates through the collection of script execution environment objects of oldestTask, adding the top-level browse context of each environment object to the top-level browse context set.
    3. With taskStartTime, now (task end time), top-level query context set, oldestTask, and executive task report.

    Long task reporting, tasks that exceed 50ms are reported to the user agent.

  10. If you’re in a window event loop,Update the rendering:
    1. Set docs as all documents of the agent associated with the current event loop. It can be sorted arbitrarily on the premise that the following conditions are met:

      • The document that is the container comes before its dependencies.
      • Documents that have the same container are sorted in the same order as they are in the container.

      As you walk through the docs below, process the documents in the same order as you sort them.

    2. Rendering Opportunity: Remove all documents in docs that are not displayed.

      If the user agent can present the contents of the browsing context to the user, the browsing context has a rendering opportunity. Hardware refresh rates and user agent performance constraints are taken into account. It is important to note that real-time content can also be rendered outside of the viewport.

      The rendering opportunity of the browsing context is determined by hardware factors (such as screen refresh rate) and other factors (such as page performance and whether the page is in the background). Renderers usually come up regularly.

      This specification does not force the selection of renderers with any particular mode. But if the browser wants a 60Hz refresh rate, the renderer will have to appear at least 60 times a second (about 16.7 milliseconds). If the browser finds that a browsing context cannot maintain this refresh rate, the rendering of that browsing context may be reduced to 30 times per second to avoid intermittent frame loss. Similarly, if the browsing context is not visible, the user agent may reduce its rendering frequency to 4 times per second, or less.

    3. If docs is not empty, set hasARenderingOpportunity to true.

    4. Render redundancy: Remove all Document objects from Docs that meet the following two criteria:

      • The user agent believes that updating the browsing context will have no noticeable effect.
      • The animation frame callback map for Document is empty.
    5. Remove documents from Docs that the user agent deems best to skip rendering updates for other reasons.

      The Render opportunity step prevents the user agent from updating the render if it cannot render new content to the user (there is no render opportunity). The “render redundancy” step ensures that the user agent updates the render when there is no new content to draw. This step prevents the Steps from accidentally executing, such as tasks that need to be executed immediately after other tasks, by ensuring that microtask execution checks can be inserted (without inserting animation frame callbacks). Specifically, the user agent might want the Timer callbacks to be merged without inserting render updates.

    6. For every fully active document in Docs, a document whose browsing context is a top-level context is quickly selected as a candidate for auto-focus.

    7. For each fully active Document in Docs, now is passed in as a timestamp when the Document resize operation is run.

    8. For each fully active Document in Docs, now is passed in as a timestamp when the document scroll operation is run.

    9. For each fully active Document in Docs, now is passed in as a timestamp when evaluating media queries and reporting changes.

    10. For each fully active Document in Docs, now is passed in as a timestamp when the animation is updated and the event is triggered.

    11. For each fully active Document in Docs, now is passed in as a timestamp when a full-screen operation of document is run.

    12. For each fully activate the document in the docs, if the user agent is detected with CanvasRenderingContext2D or OffscreenCaasRenderingContext2D associated context has been lost, Context loss steps need to be performed for such cases:

      1. If the context is CanvasRenderingContext2D, the canvas property of the context is canvas, otherwise the context’s associated OffscreenCanvas object.
      2. The context of thecontext lostSet to true.
      3. Resets the render context to the default value provided by the context.
      4. The canvas fires an event called ContextLost with a default value of true, and the result of the event is assigned to shouldRestore.
      5. ShouldRestore if it’s false, terminate the next steps.
      6. Try to restore the context by creating a backup of the context properties and associating them with the context. If that fails, abort the next steps.
      7. The context of thecontext lostFalse.
      8. Trigger a ContextreStored event on the canvas.
    13. For each fully active Document in Docs, executing the animation frame callback for that document passes in now as a time stamp.

    14. For each fully active document in Docs, an update overlap check for that document passes in now as a timestamp.

    15. Call the render time-point marking algorithm for each Document object in your docs.

    16. For each fully active document in Docs, update the document’s rendering or user interface and its browsing context to reflect the current state.

  11. If all the following conditions are true:
    1. Is a window event loop
    2. There are no tasks in the task queue for this event loop whose Document is active
    3. The microtask queue for the event loop is empty
    4. HasARenderingOpportunity is false

    So:

    1. ComputeDeadline does the following steps:
      1. Assign deadline the last idle start time of the event loop plus 50

      The 50ms limit is to ensure that the response of the page to new user actions in the future is acceptable to human perception. At 50ms, which is 20 frames per second, the human eye looks mobile.

      1. HasPendingRenders put false
      2. Loop window that iterates through the event loop, assigning to the windowInSameLoop
        1. HasPendingRenders will set false if the windowInSameLoop animation frame callback map is not empty, or if the user agent believes that windowInSameLoop may block rendering
        2. Assign the set of values for the windowInSameLoop response time mapping table to timerCallbackEstimates
        3. Iterate over timerCallbackEstimates. If a timeoutDeadline is less than the deadline, update the deadline to keep the deadline minimum.
      3. If hasPendingRenders is true then:
        1. Add a period of time (1000 / current refresh rate) to nextRenderDeadline for the last rendering opportunity of this event loop.

        Refresh rate can refer to either hardware or software implementations. For a 60Hz refresh rate, nextRenderDeadline will add approximately 16.67ms after the last render opportunity time. 2. If nextRenderDeadline is smaller than deadline, return nextRenderDeadline.

      4. Return to the deadline.
    2. Iterating through the coloop window, enter computeDeadline to perform the idle-time algorithm for each window object.
  12. If this is a worker event loop, then:
    1. If the event loop agent the scope of the global object satisfy DedicatedWorkerGlobalScope and user agent think at this point update the rendering is better, then:
      1. Assigns the current time precision to now.
      2. Perform DedicatedWorkerGlobalScope animation frames callback, now with a timestamp into the.
      3. Update the render of the dedicated worker to reflect the current state.

      Similar to the description of update rendering in the window event loop, the user agent can control the update frequency of dedicated workers.

    2. If the task queue for the event loop has no task and the WorkerGlobalScope object’s closing is true, the event loop is destroyed, the subsequent steps are terminated, and the initializing worker steps in the Web Workers chapter are performed.

When a user agent wants to perform a microtask check:

  1. Returns if the microtask execution check identifier for this event loop is true.
  2. Set the microtask execution check identifier for this event loop to true.
  3. If the microtask queue of the event loop is not empty, the loop performs the following operations:
    1. Assign the event loop’s microtask queue to oldestMicrotask.
    2. OldestMicrotask is the currently executing task for the event loop.
    3. Perform oldestMicrotask.

    A callback to the user script may be executed, cleared, and the microtask execution check algorithm invoked again, which is why we need the microtask execution check identifier to avoid reentrant.

    1. Then set the currently executing task back to NULL.
  4. Handles the event loop that the environment Settings object is responsible for, notifying promises of all reject on the environment Settings object
  5. Run the event loop [cleanup task]
  6. Run ClearKeptObjects

Weakref.prototype.deref () returns an object that remains active until a later ClearKeptObjects object is garbage collected.

  1. Set the microtask execution check identifier for this event loop to false

When an asynchronous algorithm is waiting for a return value, the user agent enlists a microtask, performs the following steps, and the algorithm pauses (when the microtask is executed, the algorithm resumes, as described below)

1. Perform the synchronization part of the algorithm. 2. Restore the asynchronous algorithm at an appropriate time based on the algorithm implementation.Copy the code

The process of rotating the event loop until the condition is satisfied is equivalent to the following steps:

  1. Treat the currently executing tasks of the current event loop as tasks

A task can also be a microtask

  1. Use the source of the task as the source
  2. Make a copy of the JS execution stack an oldStack
  3. Clear the JS execution stack
  4. Perform microtask execution check
  5. Asynchronous execution:
    1. Wait condition satisfied
    2. Join a micromission that does the following:
      1. OldStack is used as the JS execution stack
      2. Rotate the event loop, and then execute the statement following the original algorithm

      The execution of the task is resumed

  6. Pause a task until its algorithm is called to restore it

This forces the core steps of the event loop and microtask execution checks to continue

The way this specification spins the event loop is different from other algorithms in other specifications (similar to function calls in programming languages). The implementation of this specification is more like a macro, mixing in some steps and operations to save some of the upper bit and indentation.

Such as:

An algorithm does the following:

  1. Do some work
  2. Spin the event loop until some condition is reached
  3. Do other things

The above is a simplified version, after macro processing, the detailed process is as follows:

  1. Do some work
  2. Make a copy of the JS execution stack an oldStack
  3. Clear the JS execution stack
  4. Perform microtask execution check
  5. Asynchronous execution:
    1. Wait until some conditions are met
    2. Join a team to complete the following tasks:
      1. OldStack is used as the JS execution stack
      2. Do other things

Such as:

Here is a more complete example where the event loop relies on pushing a task rotation to an asynchronous queue. Version that uses the spin event loop:

  1. Asynchronous execution:
    1. Perform asynchronous work 1
    2. Join a DOM task source for a task:
      1. Perform Task 1
      2. Spin the event loop until some condition is reached
      3. Perform Task 2
    3. Perform asynchronous work 2

The detailed process is as follows:

  1. Asynchronous execution:
    1. Perform asynchronous work 1
    2. Put oldStack null
    3. Join a DOM task source for a task:
      1. Perform Task 1
      2. Make a copy of the JS execution stack an oldStack
      3. Clear the JS execution stack
      4. Perform microtask execution check
    4. Wait until some conditions are met
    5. Join a team to complete the following tasks:
      1. OldStack is used as the JS execution stack
      2. Perform Task 2
    6. Perform asynchronous work 2

For historical reasons, some algorithms in this specification require user agents to pause while performing tasks until conditions are met. Here’s what it means:

  1. Update the current state to reflect any documents related to the current state, the rendering of the browsing context, and the user interface.
  2. Wait for the condition to be satisfied. When the user agent has a suspended task, the corresponding event loop can no longer run other tasks, and any script execution in the currently running task is interrupted. However, the user agent should remain responsive to user input while paused, although nothing can be accomplished because the event loop does not process tasks at this point.

Note that pausing can be very detrimental to the user experience, especially if multiple documents share a single event loop. The specification encourages user agents to try alternatives to pauses, such as rotating event loops, even if they do not perform any tasks, simply repeating the rotation whenever possible while maintaining compatibility with existing content. If a less radical Web-compatible alternative is found, the specification will be happy to adjust.

In the meantime, implementers should be aware of the various alternatives that user agents may try, and the behavior of the event loop may change subtly, including task and microtask timing. Although implementing this would violate the true semantics of the pause operation, it should continue.

conclusion

The third section elaborates:

  • Event loop processing model
  • Implementation of event loop rotation
  • Explains how microtasks perform checks

This is the core of understanding the event loop, and the specification is like a description of code implementation. Maybe you can implement some pseudo-code in JS to help you understand.