async function as argument to setInterval nodejs - javascript

When I pass an async function as argument to setInterval, it has a strange behaviour: it works a few times but then stops without any exception.
setInterval(() => doSomethingAsync(), 1000);
I have no idea why.
I tried to implement my own setIntervalAsync but the behaviour is the same:
const sleep = ms => new Promise(res => setTimeout(res, ms));
const setIntervalAsync = async (func, interval) => {
while (true) {
await func();
await sleep(interval);
}
};
// schedule it
setIntervalAsync(async () => await doSomethingAsync(), 1000);

The problem was elsewhere and completly unrelated to setInterval or my callback.
setInterval is working fine with async functions.

Related

Calling a setTimeOut after another setTimeOut in Pure JavaScript

I have two setTimeOut here. The second setTimeOut must be called after the first time out.
setTimeout(() => {
mack.classList.add('loaded');
}, 3000);
setTimeout(() => {
location.reload(true);
}, 4000);
But I know that this is not a good solution on this. So I tried to do a Promise:
const timer = ms => new Promise(res => setTimeout(res, ms));
Promise.all([
timer(3000).then(mack.classList.add('loaded')),
timer(1000).then(location.reload(true))
])
This however, did not work. How can I fix this to make it call in order? Also, is there a version to do this using asynchronous?
You are nearly there,
To use timer the way you want, you can use async / await, just remember to be able to use await, it has to be called withing another async function, here I've just placed inside an IFFE to achieve this..
const timer = ms => new Promise(res => setTimeout(res, ms));
(async function () {
console.log('Waiting 3 seconds');
await timer(3000);
console.log('loaded');
await timer(1000);
console.log('reload');
}());
You are creating the promises, and then passing them to Promise.all, which waits for all of them simultaneously.
You want to start the second timer in response to the first timer.
This would do you you want:
timer(3000)
.then(() => {
mack.classList.add('loaded');
return timer(1000);
}).then(() => location.reload(true));
If you wanted this in an an async function:
async function example() {
await timer(3000);
mack.classList.add('loaded');
await timer(1000);
location.reload(true);
}
example().then(() => console.log('async function complete!'));

How to delay part of a function?

I would like to execute half of the a function and wait 1 second or 2 and then execute the rest, I tried like this, but I don't understand how to put a function inside another.
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
$('#music_action').click(function() {
if($(menu_start_animation).hasClass('menu_start_animation')) {
$(menu_start_animation).removeClass('menu_start_animation');
$(menu_start_animation).addClass('favorites_back_animation');
await sleep(2000);
$(menu_start_animation).removeClass('favorites_back_animation');
$(menu_start_animation).addClass('music_animation');
}
});
You just need to make your click callback async.
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
$('#music_action').click(async function () {
if ($(menu_start_animation).hasClass('menu_start_animation')) {
$(menu_start_animation).removeClass('menu_start_animation');
$(menu_start_animation).addClass('favorites_back_animation');
await sleep(2000);
$(menu_start_animation).removeClass('favorites_back_animation');
$(menu_start_animation).addClass('music_animation');
}
});
If you want to accomplish this using await, you should to make the function asynchronous or it will throw a syntax error, await wont work using regular functions. Here is an example of how to accomplish what you're trying to do asynchronously.
sleep = async (ms) => {
await new Promise((resolve, reject) => setTimeout(resolve, ms));
}
onClick = async () => {
console.log('first task')
// wait 2 seconds
await sleep(2000);
console.log('second task')
}
onClick()
However, for this use case you might not need to accomplish this asynchronously. Using setTimeout() seems async, but it runs concurrently in the background and uses less memory - asynchronous functions break a synchronous flow, but they don't necessarily execute in a concurrent order. In your case, it might be best to use a callback function.
/**
* Basic example updating a string
* In your case, the callback function would be adding and removing whatever you
* needed to in the second half of your function
*/
let word = 'init'
function sleep (callback, ms) {
setTimeout( () => {
word = 'updatedWord'
callback()
}, ms)
}
sleep(function () {
alert(word)
}, 2000)

setTimeout with Promise wrapper not working as expected with Jest async/await

Trying to do a relatively simple assertion with jest. I have the following test setup:
const sleep = ms => new Promise(res => setTimeout(res, ms));
it('should call callback after specified delay', async () => {
const mockCallback = jest.fn();
setTimeout(1000, mockCallback);
expect(mockCallback).not.toHaveBeenCalled();
await sleep(1000);
expect(mockCallback).toHaveBeenCalled();
});
When I run the test fails with the following error:
Timeout - Async callback was not invoked within the 5000ms timeout specified by jest.setTimeout.
Obviously this is nowhere near that threshold. Any idea what I'm doing wrong?
[UPDATE]
I realized previously I had called jest.useFakeTimers() before the test. After removing this and running the test again I still get a failure but it's not a timeout. Instead just simply
Expected mock function to have been called, but it was not called.
Note this is also the case when significantly increasing the sleep to 4000ms.
If instead I switch from setTimeout to
sleep(ONE_SECOND)
.then(mockCallback);
the test passes. Jest clearly modifies how setTimeout interacts in parallel with Promises, but it's not obvious as to what is going on.
You just need to pass mockCallback as the first argument to setTimeout:
const sleep = ms => new Promise(res => setTimeout(res, ms));
it('should call callback after specified delay', async () => {
const mockCallback = jest.fn();
setTimeout(mockCallback, 1000); // <= pass mockCallback as first argument
expect(mockCallback).not.toHaveBeenCalled(); // Success!
await sleep(1000);
expect(mockCallback).toHaveBeenCalled(); // Success!
});

How to write async code in JS?

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

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