JS is a single threaded that can run only sync code.
But there methods that are async by default, like setTimeout and running actions against the DB, or HTTP requests.
Those methods are added messages to the callback queue and by the time that the call stack is empty, the methods in those messages are applied to the call stack as well to be implemented.
But, how can I add a message to the callback queue by myself?
In the browser environment, there are a couple of ways:
setTimeout with a 0 interval (or similar), which queues a taskqueueMicrotask (relatively new, but easily polyfilled if the environment supports promises), which (you may have guessed!) queues a microtaskNode.js also has those, and a couple more that are more specific to its execution loop, like setImmediate (not expected to be standardized on browsers).
This article explains it very well:
https://dev.to/bipinrajbhar/how-javascript-works-web-apis-callback-queue-and-event-loop-2p3e
The setTimeout, setImmediate, http request, dom events ...etc are all
part of the Web API that is implemented in the browser and also extended in Node.JS (setImmediate for example).
They are here to add async to JS functionality. Therefore, manually adding new messages to the callback queue is possible only via the Web API.