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

JS 9 Promise Interview Questions

Table of contents 1. Multiple .catch 2. Multiple ...

SQL implementation LeetCode (176. Second highest salary)

[LeetCode] 176. Second Highest Salary Write a SQL...

MYSQL slow query and log example explanation

1. Introduction By enabling the slow query log, M...

JS implements a simple counter

Use HTML CSS and JavaScript to implement a simple...

28 Famous Blog Redesign Examples

1. WebDesignerWall 2. Veerle's Blog 3. Tutori...

js to realize the production method of carousel

This article shares the specific code for js to r...

How to use Docker containers to implement proxy forwarding and data backup

Preface When we deploy applications to servers as...

Some notes on mysql self-join deduplication

Let me briefly explain the functional scenario: T...

Windows 2019 Activation Tutorial (Office2019)

A few days ago, I found that the official version...

Table paging function implemented by Vue2.0+ElementUI+PageHelper

Preface I have been working on some front-end pro...

Example of how to change the line spacing of HTML table

When using HTML tables, we sometimes need to chan...

mysql-5.7.28 installation tutorial in Linux

1. Download the Linux version from the official w...

Knowledge about MySQL Memory storage engine

Knowledge points about Memory storage engine The ...