Why is setTimeout(fn, 0) sometimes useful?

setTimeout(callback, time)

setTimeout() is a JavaScript method you have to carefully use because it directly affects the browser or visible performance.
Fundamentally, what does setTimeout() method stand for?

from MDN,

The setTimeout() method of the WindowOrWorkerGlobalScope mixin sets a timer which executes a function or specified piece of code once the timer expires.

That’s it. it’s a timer function.
Therefore, it seems only be meaningful when it with a certain number of delay-time, but actually it is not.

There are some cases you can use setTimeout() with no delay-time (this means 0 delays).
Check out the example write down by Vue.js and Ant Design Vue.
Don’t worry, React and Angular fellas. This is just a concept.

Example

<template>
	<button @click="toggleTree">open</button>

	<div v-if="isModalOpened">
		<a-tree
			v-model="checkedKeys"
			checkable
			:expanded-keys="expandedKeys"
			:auto-expand-parent="autoExpandParent"
			:selected-keys="selectedKeys"
			:tree-data="treeData"
			@expand="onExpand"
			@select="onSelect"
		/>
	</div>
</template>

<script>
	export default {
		// ...
		methods: {
			toggleTree() {
				this.isModalOpened = !this.isModalOpened;
				document
					.querySelectorAll('.ant-tree-switcher')
					.forEach((item) => item.remove());
			},
			// ...
		},

		// ...
	};
</script>

a-tree tags are just copied from the official Ant-design-vue Website.

My object here is to remove the default Ant-design-tree open function(Triangle Icon) so that no one can expand and shrink the tree list.
I add document.querySelectorAll() in toggleTree() function which is toggling tree module. you can use refs method instead, but it should use very carefully. It sometimes messes up the work when handling third-party modules.

.ant-tree-switcher class is a default class of DOM for those tree openers.
From this code, it would catch tree openers of the Ant-design-tree module then remove them when opening the modal.

Here is Codepen example down below. Click the open button.




As you see, it doesn’t work at all.
why? Code seems fine and simple logic.

From now, it is the time to roll!
let’s call out our hero, setTimeout(fn,0) here.

<template>
	<button @click="toggleTree">open</button>

	<div v-if="isModalOpened">
		<a-tree
			v-model="checkedKeys"
			checkable
			:expanded-keys="expandedKeys"
			:auto-expand-parent="autoExpandParent"
			:selected-keys="selectedKeys"
			:tree-data="treeData"
			@expand="onExpand"
			@select="onSelect"
		/>
	</div>
</template>

<script>
	export default {
		// ...
		methods: {
			toggleTree() {
				this.isModalOpened = !this.isModalOpened;

				setTimeout(() => {
					document
						.querySelectorAll('.ant-tree-switcher')
						.forEach((item) => item.remove());
				}, 0);
			},
			// ...
		},

		// ...
	};
</script>

Nothing changed except setTimeout() wrapping document.querySelectorAll() method.

Check out the new Codepen example below.




the tree opener is gone!
Why works? or why didn’t work before?
Only difference is setTimeout().

This is all because call stack, task, and microtask of the browser.

Here is the original stack flow.

Original Flow

  1. Triggering the function toggleTree()
  2. this.isModalOpened updated the status of ant-desgin-tree module, then mounted it.
  3. While ant-desgin-tree module loaded some expensive job, document.querySelectorAll() method was called before loading default DOM such as tree opener which is the target to remove.
  4. Therefore, document.querySelectorAll() method did’t catch anything (returned undefined). Nothing could change.

with setTimeout(fn, 0)

  • Triggering the function toggleTree()
  • this.isModalOpened updated the status of ant-desgin-tree module, then mounted it.
  • While ant-desgin-tree module loaded some expensive job, (microtask) setTimeout(fn,0) method made document.querySelectorAll() method assigned as a task. Therefore, document.querySelectorAll() method waited until finishing all expensive microtask from ant-design-tree module.
  • Finally, after finishing all the jobs of the default task of ant-design-tree module, document.querySelectorAll() method was called, and caught every DOM we intended to remove.

If you still don’t understand about Browser call stack, check other posts here below the link.

Conclusion

That’s it!
That’s all about setTimeout(fn, 0) method. It makes jobs synchronously which is sometimes not available to handle by Promise.