Yes it's possible, this is even exactly what the queue a microtask task is supposed to do: push a new microtask at the end of the current job queue, i.e before the end of the current event loop.
This can be achieved by the quite new Window.queueMicrotask() method (which is currently only supported by Webkit & Blink if I'm not mistaken).
if(!window.queueMicrotask)
console.error("your browser doesn't support the queueMicrotask method");
else {
// idle
if(window.requestIdleCallback)
requestIdleCallback(() => console.log('five idle'));
// timeout
setTimeout(() => console.log('four timeout'), 0);
// message event (beginning of next event loop)
onmessage = e => console.log('three message');
postMessage('', '*');
// "queue a microtask"
queueMicrotask(() => console.log('two microtask'));
// synchronous
console.log('one sync');
}
But even in browsers that don't support this method, we have since quite long time access to other methods that will "queue a microtask".
For one, Promise.resolve
's callback will get queued as a microtask, so we could also just do
Promise.resolve()
.then(functionToBeCalledBeforeEventLoopsEnd);
// idle
if(window.requestIdleCallback)
requestIdleCallback(() => console.log('six idle'));
// timeout
setTimeout(() => console.log('five timeout'), 0);
// message event (beginning of next event loop)
onmessage = e => console.log('four message');
postMessage('', '*');
// Promise microtask
Promise.resolve().then(() => console.log('two Promise.resolve microtask'));
// obvious microtask (third because Promise's as been queued before)
window.queueMicrotask && queueMicrotask(() => console.log('three microtask'));
// synchronous code
console.log('one sync');
But even before Promises, it was already possible to queue a microtask, by using the MutationObserver API, since mutation records have to be queued as microtasks.
// idle
if(window.requestIdleCallback)
requestIdleCallback(() => console.log('six idle'));
// timeout
setTimeout(() => console.log('five timeout'), 0);
// message event (beginning of next event loop)
onmessage = e => console.log('four message');
postMessage('', '*');
// Mutation microtask
const obs = new MutationObserver(() => console.log('two mutation-microtask'));
obs.observe(test, { attributes: true });
test.className = 'foo';
// obvious microtask (third because Mutation's has been queued before)
window.queueMicrotask && queueMicrotask(() => console.log('three microtask'));
// synchronous code
console.log('one sync');
<div id="test"></div>
But beware, this also means that you can create infinite loops even if these methods are asynchronous, because the event loop would never reach its end.
const asynchronousBlockingLoop = () =>
queueMicrotask(asynchronousBlockingLoop);
And for what requestIdleCallback
does, it is waiting for when the browser has nothing to do anymore, and this can be in quite a few event loops.