Improving the performance of JavaScript execution and enhancing the browser frame rate. The functions setTimeout(..., 0) and setTimeout(..., 1) exhibit distinct behaviors

Exploring the intricacies of browser rendering mechanics and aiming to optimize various action handling on the same event while understanding how it affects repaints.

Currently, I am attempting to alter the style of a span element (action 1) and perform a more resource-intensive task by incrementing a value in a loop (action 2).

  1. It's puzzling why the change in the span's style is sometimes delayed, especially when the second action is enclosed in a setTimeout function with a 0ms delay.

  2. In contrast, setting the setTimeout delay to 1ms ensures that it does not impede the style change. What causes this difference between delays of 0 and 1?

  3. If we eliminate setTimeout and consolidate all the logic within a promise chain, the span's style change is consistently obstructed. Does this imply that chained microtasks, which operate synchronously, behave akin to a synchronous loop in this scenario? Are they hindering repaints and unsuitable for such optimizations?

P.S. Any recommendations for comprehensive resources addressing optimization of script execution across diverse rendering frames would be greatly appreciated!

document.addEventListener("click", (evt) => {
  if (!evt.target.matches("span")) { return; }
  evt.target.style.color = "red";

  setTimeout(() => {
    const loopTimes = 900000;

    const result = Array(loopTimes)
      .fill()
      .reduce((acc, val, i) => {
        return acc.then((val) => {
          return val + 1;
        });
      }, Promise.resolve(0));

    result.then((endResult) => console.log(endResult));
  }, +evt.target.dataset.timeout);
});
<span data-timeout="0">click for red (0ms)</span><br>
<span data-timeout="1">click for red (1ms)</span>

Answer №1

This particular behavior is specific to Chrome and seems to have surfaced recently with the implementation of the removal of the default 1ms timeout for setTimeout.
I received a comment related to a "bug" I reported, which explains:

In Chromium's task scheduling, task queues are executed using two methods: PostImmediateTaskImpl and PostDelayedTaskImpl [1], where setTimeout(fn2, 0); in the test case is treated as post immediate task [...]

This change means that setTimeout(fn, 0) now has an ultra high priority and will always be executed before the next animation frame (typically occurring right after a click event due to mouse events being synchronized with screen refresh rate).

Based on your inquiry, it appears you are interested in understanding the rendering process at a higher level.
To summarize, Events (native) are triggered from tasks queued by the event-loop, which selects one to execute during each of its iterations.
Some iterations of this event-loop are special because the monitor sends a V-Sync signal indicating the browser can update data. This is known as a rendering frame.
During a rendering frame, the browser executes specific callbacks (such as animation frames, resize or scroll events, CSS animations, page rendering, etc.). Blocking the event-loop prevents these actions from executing within the rendering frame, causing the browser to wait until the script completes (or potentially terminating it if it takes too long).
Given that the click event occurs close to the next painting frame, a 1ms timeout allows the browser to enter the rendering frame before scripts block it.

Microtasks maintain their own microtask-queue, processed whenever the JS callstack clears, typically following each callback (among other points in the event-loop). Once this queue is emptied, control returns to the event-loop, including any added microtasks since the last visit. Consequently, something like

function lock() { Promise.resolve().then(lock) }
can stall the browser akin to a while(1) {} loop.

It's worth noting that setTimeout is largely independent of the event loop's rendering phase.

To delve into rendering specifics, focus on animation frames by utilizing the requestAnimationFrame() method, scheduling a callback just prior to rendering.

In this scenario, even Chrome's unconventional setTimeout(fn, 0) triggers only after rendering:

document.addEventListener("click", (evt) => {
  // wait for the next animation frame
  requestAnimationFrame(() => {
    if (!evt.target.matches("span")) { return; }
    evt.target.style.color = "red";

    setTimeout(() => {
      const loopTimes = 900000;

      const result = Array(loopTimes)
        .fill()
        .reduce((acc, val, i) => {
          return acc.then((val) => {
            return val + 1;
          });
        }, Promise.resolve(0));

      result.then((endResult) => console.log(endResult));
    }, +evt.target.dataset.timeout);
  });
});
<span data-timeout="0">click for red (0ms)</span><br>
<span data-timeout="1">click for red (1ms)</span>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Pictures in the portfolio failing to load on Mozilla Firefox

Having some trouble with my WordPress website bmfconstruction.com.au/new/. In the project section, all images are loading correctly in Chrome, Opera, and IE. However, when it comes to Firefox, none of the images seem to be showing up. I've tested this ...

Freezing browser during an active AJAX call

I am currently working on an ASP.NET Web App and have noticed a peculiar issue. Whenever I make a simple ajax call (as shown below), the web application becomes unresponsive to any actions I try to perform on a different browser. $.ajax({ type: "G ...

Utilize Vue to access and read a file stored in the current directory

I am attempting to retrieve data from a text file that is located in the same directory as my .vue file. Unfortunately, I am encountering an issue where the content of the text file is not being displayed in both Chrome and Firefox. Instead, I am seeing th ...

Numerous web worker asynchronous requests are being made, but not all are coming back

Within my web worker script, I am utilizing the following code: self.addEventListener('message', function(e){ try { var xhr=new XMLHttpRequest() for(var i = 0; i < e.data.urls.length;i++){ xhr.open('GET&apos ...

Display Fetched Data Using Ajax When Button is Clicked

I am facing an issue with updating my webpage with new values retrieved from a Summary Report data handler (dhSummaryReport.ashx). The JSON data returned by the handler looks like this: {"iTotalRecords":1,"aaData":[{"val1Total":3,"val2Total":6,"val3Total" ...

The recursive function halted its execution following the initial iteration

Situation To ensure server efficiency, I have set up a system where multiple websites are updated using Ajax calls sequentially. I've created a recursive function that triggers itself every time an Ajax call is successfully completed. Problem Howev ...

Encountered an error: [$injector:modulerr] while using AngularJS

I need help creating a registration form using AngularJS. I am encountering an error in my app.js file: (function (){ 'use strict'; angular.module('modulemain',[]).controller('ModuleController',ModuleController); ...

It's important to understand how to correctly send state values from a parent component to a child component

I have a tab menu in my code and I am looking to implement a feature where tabs can be switched using a button. The challenge is passing the values of these tabs into the onclick function of the button in order to navigate between them. Here is an example ...

Aligning a rotated paragraph in the middle of its parent container

Here is my current progress: http://jsfiddle.net/2Zrx7/2/ .events{ height:100px; position: relative; } .tt_username{ position: absolute; top:0px; height: 100%; width: 30px; background: #ccc; text-align: center; } .tt_usern ...

Please input only 0s and 1s into the designated field

How can I modify this code to only accept 0 and 1 in the input field, while also adding spaces after every 4 digits? Currently, it accepts all digits. I'm struggling with creating a pattern that restricts it to just 0 and 1. document.getElementById(&a ...

I am experiencing issues with the rendering of my q-btn elements in Quasar and they are

I recently started using Quasar and encountered an issue with my q-btn component buttons displaying white backgrounds at random, despite having added a background-color in the external stylesheets. Here are some examples of this perplexing problem: https ...

Creating a real-time clock in a single line console log format using NodeJS

To create a live clock in a single line display within a browser, we can utilize setInterval and manipulate the content inside an HTML element like so: var span = document.getElementById('span'); function time() { var d = new Date ...

Issue encountered while utilizing Mongoose ArrayFilters for updating a nested subdocument

I am working with a Mongoose collection and need to update a nested subdocument within it. Here is the basic structure: The collection has a parent entry (Map) This entry contains an array of children (Phases) Each child has one or more grandchildren (S ...

What steps can I take to enhance the security of my MEAN stack application?

As a front-end developer venturing into back-end development, I have successfully built my first back end using the MEAN stack. Now that my core functionality is nearly complete, I am turning my attention to security. Currently, I am only utilizing jsonweb ...

"jQuery's input type property is not functioning properly when attempting to append new

I am facing an issue with my dynamic table structure and the add row option. Whenever I click on add, a new row is created. However, the problem arises when the $("[name*=vehicle_type]").change(function () does not work for appended input fields. It only ...

Encountered CSRF validation error while working with a Python Django backend in conjunction with React frontend using Axios for making POST requests

I recently completed a tutorial at and now I'm attempting to add a POST functionality to it. Despite obtaining the csrf from cookies and including it in the "csrfmiddlewaretoken" variable alongside a test message in json format for the axios function ...

The setTimeout function does not seem to be triggering within Protractor when utilizing JavaScript

I have encountered an issue with a function I created that is meant to read a file after a delay of 2 minutes. function checkInLogs(logFilPath, logMessage){ setTimeout(() => { fs.readFile(logFilPath, 'utf8', function (err,data){ ...

The absence of an index signature in GenericType is causing a missing declaration of the expected key/value type in Flow type

I am encountering difficulties accessing an object property dynamically with a Generic Type. Below is the code snippet: import React, { useState } from 'react' function useForm<FormValues>(initialValues: FormValues) { const [formValue ...

Is it feasible to implement hot swapping in Node.js?

Can Node.JS support or mimic hot swapping code functionality? If so, what is the process? Hot swapping, also known as hot plugging, involves replacing or adding components without system shutdown. Erlang and Lisp have native support for hot swapping. ...

Unable to execute commitlint in husky along with a different custom command

Is it possible to set up two precommit hooks with husky? Specifically, I want to integrate commitlint along with a custom script specified in my package.json. After installing husky and creating a pre-commit script in the .husky folder, here is what I have ...