You can't¹. To do asynchronous tasks, you need some way to run the code on another thread, and that is impossible from within JavaScript.
setTimeout
is asynchronous because it triggers an internal function written in some low level language (probably C++), which will then utilize hardware timers to trigger the callback to be called after some time.
fetch(...)
for example is also asynchronous, because it will start an internal thread that writes and reads through your network card, and then calls back to the JavaScript engine when the request was done.
¹ that's a bit oversimplified. You could start multiple WebWorkers (multiple JavaScript engines running concurrently) to get some kind of multithreading. While the code still runs synchronous per thread, the two threads can execute concurrently and exchange information via messages, through that you can get asynchronous behaviour.
// worker.js
onmessage = function({ data }) { // called when messages from the other thread come in
let start = Date.now();
while(start + 3000 > Date.now()); // some long running action
postMessage("done"); // go back to the other thread
}
// main.js
const otherThread = new WebWorker("worker.js");
otherThread.postMessage("go!"); // launch async task in other thread
otherThread.onmessage = function({ data }) { // receive result
console.log(data); // "done"
});
WebWorker documentation