Several ways to implement 0ms delay timer in js

Several ways to implement 0ms delay timer in js

These two days I saw an article introducing "How to implement timely setTimeout? 》, the article originated from an interview question: Is there any way to make setTimeout on time? For more details, please refer to Appendix [1]. After reading it, I became interested in exploring the setTimeout function, so I re-checked the relevant documents on MDN. It mentioned that [minimum delay >= 4ms], so a 0ms delay timer cannot be implemented using setTimeout. If you want to implement it, a reference link [2] is provided. The author's implementation idea is to simulate it through postMessage, bypassing the limitation of setTimeout, and thus implementing a 0ms delay timer. In short, it is to start a macro task to execute the callback. Let's take a look at how it is implemented:

(function() {
 var timeouts = [];
 var messageName = "zero-timeout-message";
 // Like setTimeout, but only takes a function argument. There's
 // no time argument (always zero) and no arguments (you have to use a closure)
 function setZeroTimeout(fn) {
  timeouts.push(fn);
  window.postMessage(messageName, "*");
 }
 function handleMessage(event) {
  if (event.source == window && event.data == messageName) {
   event.stopPropagation();
   if (timeouts.length > 0) {
    var fn = timeouts.shift();
    fn();
   }
  }
 }

 window.addEventListener("message", handleMessage, true);

 // Add the one thing we want added to the window object.
 window.setZeroTimeout = setZeroTimeout;
})();

The author also provides a demo page [3]. By comparing it with setTimeout(0), the execution results in my browser are as follows:

100 iterations of setZeroTimeout took 15 milliseconds.
100 iterations of setTimeout(0) took 488 milliseconds.

According to the comparison results, setZeroTimeout executes hundreds of times faster than setTimeout, which is a huge improvement. What I want to discuss today is what other ways can be used to implement a 0ms delay timer in addition to the above method. First, we must make sure that our custom timer is asynchronous, and secondly, it is executed as early as possible. Speaking of asynchrony, js provides several solutions, which we can verify one by one.

Before discussing various implementation methods in depth, the setTimeout comparison version provided by the agreement is as follows. The execution time of the custom implementation schemes will be compared with the setTimeout version. The code is relatively simple:

(function() {
 let i = 0;
 const start = Date.now();
 function test() {
  if(i++ < 100) {
   setTimeout(test);
  } else {
   console.log('setTimeout execution time:', Date.now() - start);
  }
 }
 setTimeout(test);
})();

queueMicrotask

The queueMicrotask API can add a microtask. It is relatively simple to use. Just pass a callback function. The specific implementation is as follows:

(function() {
 function setZeroTimeout(fn) {
  queueMicrotask(fn);
 }
 let i = 0;
 const start = Date.now();
 function test() {
  if(i++ < 100) {
   setZeroTimeout(test);
  } else {
   console.log('setZeroTimeout execution time:', Date.now() - start);
  }
 }
 setZeroTimeout(test);
})();

By comparing with the setTimeout version, the final result is as follows:

setZeroTimeout execution time: 2
setTimeout execution time: 490

There is a detailed introduction to this API on MDN, so I will not go into details here. According to the specification document, in most cases, it is recommended to use APIs such as requestAnimationFrame() and requestIdleCallback(), because queueMicrotask will block rendering, which is not a good practice in many cases.

async/await

Async/await is already essential for front-end developers, and we can also use it here to achieve:

(function() {
 async function setAsyncTimeout(fn) {
  Promise.resolve().then(fn);
 }
 let i = 0;
 const start = Date.now();
 async function test() {
  if (i++ < 100) {
   await setAsyncTimeout(test);
  } else {
   console.log('setAsyncTimeout execution time:', Date.now() - start);
  }
 }
 setAsyncTimeout(test);
})();

By comparing with the setTimeout version, the final result is as follows:

setAsyncTimeout execution time: 2
setTimeout execution time: 490

If you don't mind the trouble, you can also use Promise to implement it. In fact, they are all similar, just a little more code, which is omitted here.

MessageChannel

MessageChannel allows us to create a new message channel and send data through its two MessagePort properties. MessageChannel provides the concept of ports to achieve communication between ports, such as communication between workers/iframes.

(function() {
 const channel = new MessageChannel();
 function setMessageChannelTimeout(fn) {
  channel.port2.postMessage(null);
 }
 channel.port1.onmessage = function() {
    test();
 };
 let i = 0;
 const start = Date.now();
 function test() {
  if(i++ < 100) {
   setMessageChannelTimeout(test);
  } else {
   console.log('setMessageChannelTimeout execution time:', Date.now() - start);
  }
 }
 setMessageChannelTimeout(test);
})();

By comparing with the setTimeout version, the final result is as follows:

setMessageChannelTimeout execution time: 4
setTimeout execution time: 490

The third method takes longer to run than the previous two, because the MessageChannel generates macrotasks, while the other two methods are microtasks. Microtasks are executed first and will block the main thread, so they take longer.

at last

This article provides three implementation methods, all of which are based on the asynchronous solution provided by js. The implementation itself is not complicated.

appendix

【1】https://mp.weixin.qq.com/s/QRIXBoKr2dMgLob3Atq9-g
【2】https://dbaron.org/log/20100309-faster-timeouts
【3】https://dbaron.org/mozilla/zero-timeout

This concludes this article about several ways to implement 0ms delay timer in js. For more relevant js delay timer content, please search for previous articles on 123WORDPRESS.COM or continue to browse the following related articles. I hope you will support 123WORDPRESS.COM in the future!

You may also be interested in:
  • Detailed explanation of JavaScript timers
  • JavaScript Timer Details
  • JavaScript timer to achieve limited time flash sale function
  • JavaScript timer to achieve seamless scrolling of pictures
  • Using JS timer to move elements
  • Detailed explanation of the JavaScript timer principle

<<:  Detailed instructions for installing SuPHP on CentOS 7.2

>>:  MySQL 8.0.16 installation and configuration graphic tutorial under macOS

Recommend

Detailed explanation of mysql exists and not exists examples

Detailed explanation of mysql exists and not exis...

How to use mqtt in uniapp project

Table of contents 1. Reference plugins in the uni...

Pros and Cons of Vite and Vue CLI

There is a new build tool in the Vue ecosystem ca...

Tips for making web table frames

<br />Tips for making web table frames. ----...

Introduction to JavaScript conditional access attributes and arrow functions

Table of contents 1. Conditional access attribute...

Using docker command does not require sudo

Because the docker daemon needs to bind to the ho...

Design theory: people-oriented green design

Reflections on the two viewpoints of “people-orie...

Complete steps to install boost library under linux

Preface The Boost library is a portable, source-c...

Implementation of fuzzy query like%% in MySQL

1, %: represents any 0 or more characters. It can...

Example of how to set div background transparent

There are two common ways to make div background ...

Detailed explanation of how to view the number of MySQL server threads

This article uses an example to describe how to v...

How to communicate between WIN10 system and Docker internal container IP

1. After installing the Windows version of Docker...

Detailed analysis of javascript data proxy and events

Table of contents Data Brokers and Events Review ...

How to use an image button as a reset form button

When we make a form, we often set a submit button ...