How to write async code in JS? - javascript

JavaScript is single thread language, which means all user-written code will run in the main thread. For example, in Node.js, the async IO read is an async operation, it runs in a worker thread, but the callback which developer has written run in the main thread as other JS code. So if I identify one JS function with async, it actually did not run in other thread, more important, an async also doesn't mean non-blocking.
const sleep = (wait) => {
const start = new Date().getTime();
while (new Date().getTime() - start <= wait) {}
}
const async_print1 = async () => {
setTimeout(() => {
sleep(2000);
console.log('async_print1'); /// in 4s.
}, 2000);
}
const async_print2 = async () => {
setTimeout(() => {
sleep(1000);
console.log('async_print2'); /// in 5s.
}, 2000);
}
const sync_print = () => {
sleep(1000);
console.log('sync_print'); /// in 1s.
}
async_print1();
async_print2();
sync_print();
The output:
[00:00] <program start>
[00:01] sync_print
[00:04] async_print1
[00:05] async_print2
[00:05] <over>
Fisrt, sync_print run in main thread, it sleep 1s then print. Then, two timer start, after 2s, run loop need call two callback, the two callback both run in main thread, so they're blocking operations.
My question is how to make two sleep() run in other thread? Or just can not?
**Updated my question **
Sorry for my poor english and expression, I finally understand. Thanks you. Is it possible to execute Javascript functions with multi-threads

There is no way to turn synchronous code into asynchronous code. If the event loop is busy running your while loop (which is blocking code), then it is going to be too busy to do anything else. The async keyword just makes a function return a promise (and allows you to use await inside it).
You can shunt code off into another thread using Web Workers.

You probably don't need web workers yet. It looks like you forgot await altogether -
const sleep = ms =>
new Promise (r => setTimeout (r, ms))
const asyncPrint1 = async () =>
{ await sleep (2000)
console.log ("async print 1")
}
const asyncPrint2 = async () =>
{ await sleep (2000)
console.log ("async print 2")
}
const syncPrint = () =>
{ console.log ("sync print")
}
const main = async () =>
{ await asyncPrint1 () // ...2 seconds
await asyncPrint2 () // ...4 seconds
await sleep (1000) // ...5 seconds
syncPrint ()
}
main ()
.then (console.log, console.error)
// async print 1
// async print 2
// sync print
Inside an async function, you can await as many other async calls as you want -
const sleep = ms =>
new Promise (r => setTimeout (r, ms))
const main = async () =>
{ console.log ("begin")
await sleep (1000)
console.log ("1 second has passed")
await sleep (1000)
await sleep (1000)
console.log ("3 seconds have passed")
await sleep (1000)
await sleep (1000)
await sleep (1000)
console.log ("6 seconds have passed")
}
main ()
.then (console.log, console.error)
// begin
// 1 second has passed
// 3 seconds have passed
// 6 seconds have passed
// undefined

Related

How does an async function called with await can block the event loop?

In the following code, the 'Hey, timeout executed!' is only printed after the loop in the longLoop function is finished.
setTimeout(() => console.log('Hey, timeout executed!'), 100);
let longLoop = async () => {
for (i = 0; i < 1000000000; i++) {
}
}
let exec = async () => {
await longLoop();
console.log('Loop finished')
}
exec()
However, when I change the longLoop to have an await inside it, the 'Hey, timeout executed!' will print as soon as the await in the loop is reached, like in the following:
setTimeout(() => console.log('Hey, timeout executed!'), 100);
let longLoop = async () => {
for (i = 0; i < 1000000000; i++) {
await new Promise(r => setTimeout(r, 100));
}
}
let exec = async () => {
await longLoop();
console.log('Loop finished')
}
exec()
Why does in the first example the event loop seems to be blocked even if I call the longLoop from the exec using an await? And why it doesn't get blocked in the second example just by using an await inside the loop?
I'm just starting in JavaScript, so I thought that just by a function being async and being called with an await inside another async function, it would be executed in a way that the event loop would not be blocked. Due to that, I'm having kinda a hard time to understand the flow of execution.
This is a case of microtasks vs (macro)tasks. Promises and async/await enter the microtask queue. setTimeout callbacks enter the (macro)task queue. microtasks get run before (macro)tasks (when the stack is empty to be precise), that's why you see the async/await functions get executed before the setTimeout callback
See also:
Difference between microtask and macrotask within an event loop context
https://stackoverflow.com/a/71195496/4651083

Is the map method parallel? [duplicate]

This question already has answers here:
How is Javascript single threaded?
(6 answers)
Closed 2 years ago.
Is the method [1, 2, 3].map(item=>f(item)) execute f(1), f(2), f(3) parallelly? Or execute like "f(1), then f(2), then f(3)"?
Actually it execute "f(1), then f(2), then f(3)"?
Because JS is single-threaded. So if the f function is an async task (setTimeout, Ajax call, etc..), then it can be scheduled to waits parallelly combined with promise.all. Otherwise, it still has to execute synchronously.
One more thing to keep in mind is when you use await inside async function,
It does only waits parallel when you use promise.all.
var tick;
const log = (v) => console.log(`${v} \n Elapsed: ${Date.now() - tick}`);
const f = (number) => new Promise(resolve => {
setTimeout(() => log(number), 1000);
});
async function Run(){
tick = Date.now();
await Promise.all([f(1), f(2), f(3)]);
}
function Run_2(){
tick = Date.now();
[f(1), f(2), f(3)];
}
Run();
Run_2();
While you await each f, it takes 3s in total.
var tick;
const log = (v) => console.log(`${v} \n Elapsed: ${Date.now() - tick}`);
const f = (number) => new Promise(resolve => {
setTimeout(() => resolve(number), 1000);
});
async function Run(){
tick = Date.now();
let a = await f(1);
let b = await f(2);
let c = await f(3);
log([a, b, c]);
}
Run();
However, Promise.all can only waits async tasks in parallel. It can't convert a synchronous CPU block code into a separate thread or anything.

Periodically resolving a promise in javascript

Let’s say I want to resolve some promise every 2 seconds AFTER the function is run.
I tried a pattern like this at first: setInterval(MyAsyncFunction, 2000), but of course this does not wait for the promise to resolve before calling the function again.
I also tried setInterval(async () => await MyAsyncFunction(), 2000), but this is basically the same thing since I’m just wrapping it in another promise that will resolve while the setInterval is ticking down.
Is there a way to do what I am trying to do with setInterval or is there some alternative way to do this using setTimeout?
A Promise resolves once only, so therefore cannot, by definition, resolve periodically.
If you define a delay function, which returns a promise that resolves after timeout ms...
const delay = timeout => new Promise(resolve => setTimeout(resolve, timeout));
you could use it in an async loop:
async function foo(){
for(let i = 0; i < 100; ++i){
console.log("azrael");
await delay(2000);
}
}
If what you want is to call async function, then await for it, then wait extra fixed time, then repeat, you may approach it following ways.
You can call setTimeout from that function itself, so it will schedule its next run when it's finished:
async function myAsyncFunction() {
return new Promise(resolve => setTimeout(() => {
resolve('result'); // resolves, then
setTimeout(async () => { // schedules
let result = await myAsyncFunction(); // self-call
console.log('result') // and handles the result
}, 2000); // after extra 2s
}, 2000)); // imitates async function with 2s delay
}
async function main() {
let firstResult = await myAsyncFunction();
console.log(firstResult); // handles first call
}
More clean way is to wrap it in another async function with same setTimeout inside:
async function myAsyncFunction() {
// imitates async function
return new Promise(resolve => setTimeout(() => resolve('result'), 2000));
}
async function delayLoop() {
let result = await myAsyncFunction(); // waits for it
console.log(result);
setTimeout(delayLoop, 2000); // calls itself after extra 2s
}
delayLoop();
This way you do not have to modify original async function code and put callback right in there.
Both examples will await for async function for 2s before first result, and for 4s between calls - 2s async function delay + 2s extra delay after it.

JavaScript Sequential function execution for multiple functions

I (first time JavaScript user since yesterday) managed to get JavaScript to run functions in sequential execution order (see code below) (credit to #CertainPerformance). I need to use the fastFunction in multiple slowFunctions. The current solution does not seem DRY (do not repeat yourself) to me and at the same time it does not guarantee the exectution order of slowFunction1 and then slowFunction2. What is the DRY solution to this problem in JavaScript? Can I force JavaScript to always run in sequential mode by some configuration? Using nested callbacks does not seem to be the most intelligent solution to me.
function fastFunction(message) {
console.log(message);
}
function slowFunction1(callback, message) {
setTimeout(() => {
console.log('slowFunction1!');
callback(message);
}, 10000);
}
function slowFunction2(callback, message) {
setTimeout(() => {
console.log('slowFunction2!');
callback(message);
}, 1000);
}
slowFunction1(fastFunction, 'fast_Function');
slowFunction2(fastFunction, 'fast_Function');
With async/await you can sequence asynchronous tasks as follows:
// Very handy utility function to get a promise that resolves after a given delay
const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
function fastFunction(message) {
console.log(message);
}
async function slowFunction1(callback, message) {
console.log('slowFunction1!');
await delay(2000); // two seconds
callback(message);
}
async function slowFunction2(callback, message) {
console.log('slowFunction2!');
await delay(1000); // one second
callback(message);
}
(async function() {
// Put all your logic here, and await promises...
await slowFunction1(fastFunction, 'fast_Function');
await slowFunction2(fastFunction, 'fast_Function');
})(); // execute immediately
Now you will have the delays happening one after the other completes, so 2+1=3 seconds in (approximate) total execution time.
This mimics most what you had as pattern, but once you are using promises, you don't need the callback pattern anymore and can do it like this:
// Very handy utility function to get a promise that resolves after a given delay
const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
function fastFunction(message) {
console.log(message);
}
(async function() {
console.log('slow part 1');
await delay(2000); // two seconds
fastFunction('fast_function');
console.log('slow part 2');
await delay(1000); // one second
fastFunction('fast_function');
})(); // execute immediately

Why does my timeout wait until after the await completes but my log does not?

If I have something like this setup:
<-- language: lang-javascript -->
console.clear();
// noprotect
const fetchSomething = () => new Promise((resolve) => {
setTimeout(() => resolve('future value'), 500);
});
async function asyncFunction() {
const result = await fetchSomething();
console.log('waiting');
setTimeout(()=>console.log('waiting?'), 250);
return result + ' 2';
}
asyncFunction().then(result => console.log(result));
And my output looks like:
"waiting"
"future value 2"
"waiting?"
I would expect the waiting? to execute before the result completes, but for some reason it waits on the function. What makes one wait but the other execute?
It is a feature of Asynchronous programming.
You have to add await and wrap your setTimeout(()=>console.log('waiting?'), 250); with a function which returns Promise in order to make it look like it was evaluated continuously.
Something like:
await ((ms) =>{
console.log('waiting?');
return new Promise(resolve => setTimeout(resolve, ms));
})(250);
Or:
await (() => new Promise(resolve => setTimeout(()=>{
console.log('waiting?');
resolve();
}, 250)))();
Mind you, JS has a single threaded run-time engine, so it interrupts evaluation when original script reaches it's end.
And function in setTimeout(function, timeout) is evaluated by JS when it has a first chance and time is right.
So your function was interrupted twice and was resumed twice.
The call to log "waiting?" is started by a setTimeout after the await has finished, so after the 500ms in fetchSomething have already passed. It will only execute 250ms after fetchSomething has returned. That is enough time for asyncFunction to return.
If you want to see a different behaviour, start the timer for logging "waiting?" before calling await:
async function asyncFunction() {
setTimeout(()=>console.log('waiting?'), 250);
const result = await fetchSomething();
console.log('waiting');
return result + ' 2';
}

Categories