How can I ensure that the second function call is started after the first one finished execution?
const asyncFuntion = async (callNumber: string, timeout: number) => {
await setTimeout(() => {
console.log(`call: ${callNumber}`, timeout);
}, timeout);
};
asyncFuntion("1", 100);
asyncFuntion("2", 50);
Console output right now:
call: 2 50
call: 1 100
Desired console output:
call: 1 100
call: 2 50
await will have no effect on setTimeout() as it does not returns a promise.
So, the solution to this problem is to wrap setTimeout using Promise.
Also, if you want first function call to be executed before the second, create promises and resolve them before you make a call to second one and make use of await for the first function call.
Have a look at the following implementation:
const asyncFuntion = async (callNumber, timeout) => {
return new Promise( async (resolve, reject) => {
await setTimeout(() => {
console.log(`call: ${callNumber}`, timeout);
resolve();
}, timeout);
});
};
async function test() {
/*Write the function calls in the order you want them to get executed*/
await asyncFuntion("1", 100);
await asyncFuntion("2", 50);
}
test();
PS: You should also await the second call so that the promise returned by test() fulfills at the appropriate time, not in the middle of running the two timeouts. Credits #Bergi.
Two matters to notice.
Using await in front of setTimeout has no effect. It only works for functions which return a promise.
The execution order of the setTimeout solely depend on the interval - not on another iteration.
There is no way an interval of 100ms do something before the interval of 50ms. If you want the second call to be waiting until the completion of the first, create a promise and resolve it before you call the second one.
Another way of doing it to manipulate the setTimeout timer, its not as elegent but works.
let timer = 0
const asyncFuntion = (callNumber, timeout) => {
timer += timeout
setTimeout(() => {
console.log(`call: ${callNumber}`, timeout);
}, timer);
};
asyncFuntion("1", 100);
asyncFuntion("2", 50);
Related
I have tried a thousand ways to do this, but none work properly. Here msg1 and msg2 are stand ins for functions that I want to call synchronously (first one then the other). A for loop produces "unknown" in the second loop. After searching, I think this is the right way to do it, but I am still not there. The answer that I would like to get is:
msg1
msg2
but I am getting
msg1
msg1
Here is my code
var data = {
"messages": ["msg1", "msg2"],
"i": -1
}
const increment = async () => {
data.i = await data.i + 1;
};
const test = async () => {
data.messages.forEach((fcn) => {
increment(data)
.then(() => {
console.log(data.messages[data.i])
return data
})
})
};
test(data)
UPDATE
the asynchronous answer from jfriend00 (below) works!!! BUT, when used in my actual code, it runs one extra time producing "undefined" in the final loop. I have tried changing the starting value for i, different formulations of the for loop and adding a check for the value "undefined" (as below), but the undefined value remains goes undetected. I have also lengthened the delay (unsuccessful). How can I ensure that the loop only runs once per message?
if (data.i !== undefined) {
console.log(data.messages[data.i])
}
Since there's nothing asynchronous here, you can just remove all the async, await and .then() and then run your synchronous code (you can run each of these snippets directly, right in the browser to see what they send to the console).
var data = {
"messages": ["msg1", "msg2"],
"i": -1
}
const increment = () => {
++data.i;
};
const test = () => {
data.messages.forEach((fcn) => {
increment(data)
console.log(data.messages[data.i])
});
};
test(data)
If you want the increment() function to be an actual asynchronous operation and you want to sequence the calls to increment() then, you have to ditch .forEach() and use a for loop and use await appropriately and insert something that is actually asynchronous into the increment() function (I inserted a promise-based delay):
function delay(t) {
return new Promise(resolve => {
setTimeout(resolve, t);
});
}
var data = {
"messages": ["msg1", "msg2"],
"i": -1
}
const increment = async () => {
await delay(100);
++data.i;
};
const test = async () => {
for (let msg of data.messages) {
await increment(data);
console.log(data.messages[data.i])
}
};
test(data)
Comments
Your existing code is misusing async, await and .then(). There are no asynchronous operations anywhere in your existing code so there's no reason for any of async, await and .then() as that just makes things more complicated than required.
In addition, you only ever use await when you are awaiting a promise. So, data.i = await data.i + 1; is misguided. It will run, but the await doesn't do anything useful - it only makes the code more complicated.
the piace of code with if condition will not executed and the parent function will be executed after promise, but I dont understand why
let sql = `SELECT *
FROM ${table}
WHERE trader = '${trader}'
AND pair = '${data.symbol}'`;
console.log(sql)
let resp = await new Promise((resolve, reject) => {
db.all(sql, function (err, rows) {
console.log("err2")
console.log(err)
console.log("rows2")
console.log(rows)
return resolve(rows)
})
})
if (resp[0]) { // <------- this will be executed after
if (data.amount > resp[0].amount) {
console.log('amount cambiato, comprato')
// BUY Position
await updatePosition(data, trader, 'buy');
}
if (data.amount < resp[0].amount) {
console.log('amount cambiato, sellato')
// BUY Position
await updatePosition(data, trader, 'sell');
}
if (data.amount == resp[0].amount) {
// BUY Position
console.log('amount IDENTICO');
await setCheckedTraderCoin(trader, data.symbol)
}
}
why this?
This has to be in an async function since you're using await. Thus, it returns a promise.
So, the caller must use await or .then() on that returned promise so it will also wait for the work to be done here.
ALL async functions return their promise upon the first await in the function. That's why the caller has to also wait for that promise to resolve - otherwise it runs off and executes before any of the asynchronous work in this function is complete (as you've observed).
So, the solution to your caller running before the asynchronous work in this function has completed is for the caller to wait (using either .then() or await) for the returned promise to resolve before it does anything further.
Also, you need error handling on the promise that wraps db.all() so you reject that promise if there's an error:
let resp = await new Promise((resolve, reject) => {
db.all(sql, function (err, rows) {
if (err) {
reject(err);
} else {
resolve(rows)
}
});
});
FYI, it would be preferable to use a driver for your database that directly supports promises so you don't have to manually wrap each call in a promise yourself. Most database interfaces support promises by now. Sometimes, you need a different version of the driver - sometimes it's already available in the one you're using. It depends upon which database/driver you are currently using.
I'm writing an HTTP API with expressjs in Node.js and here is what I'm trying to achieve:
I have a regular task that I would like to run regularly, approx every minute. This task is implemented with an async function named task.
In reaction to a call in my API I would like to have that task called immediately as well
Two executions of the task function must not be concurrent. Each execution should run to completion before another execution is started.
The code looks like this:
// only a single execution of this function is allowed at a time
// which is not the case with the current code
async function task(reason: string) {
console.log("do thing because %s...", reason);
await sleep(1000);
console.log("done");
}
// call task regularly
setIntervalAsync(async () => {
await task("ticker");
}, 5000) // normally 1min
// call task immediately
app.get("/task", async (req, res) => {
await task("trigger");
res.send("ok");
});
I've put a full working sample project at https://github.com/piec/question.js
If I were in go I would do it like this and it would be easy, but I don't know how to do that with Node.js.
Ideas I have considered or tried:
I could apparently put task in a critical section using a mutex from the async-mutex library. But I'm not too fond of adding mutexes in js code.
Many people seem to be using message queue libraries with worker processes (bee-queue, bullmq, ...) but this adds a dependency to an external service like redis usually. Also if I'm correct the code would be a bit more complex because I need a main entrypoint and an entrypoint for worker processes. Also you can't share objects with the workers as easily as in a "normal" single process situation.
I have tried RxJs subject in order to make a producer consumer channel. But I was not able to limit the execution of task to one at a time (task is async).
Thank you!
You can make your own serialized asynchronous queue and run the tasks through that.
This queue uses a flag to keep track of whether it's in the middle of running an asynchronous operation already. If so, it just adds the task to the queue and will run it when the current operation is done. If not, it runs it now. Adding it to the queue returns a promise so the caller can know when the task finally got to run.
If the tasks are asynchronous, they are required to return a promise that is linked to the asynchronous activity. You can mix in non-asynchronous tasks too and they will also be serialized.
class SerializedAsyncQueue {
constructor() {
this.tasks = [];
this.inProcess = false;
}
// adds a promise-returning function and its args to the queue
// returns a promise that resolves when the function finally gets to run
add(fn, ...args) {
let d = new Deferred();
this.tasks.push({ fn, args: ...args, deferred: d });
this.check();
return d.promise;
}
check() {
if (!this.inProcess && this.tasks.length) {
// run next task
this.inProcess = true;
const nextTask = this.tasks.shift();
Promise.resolve(nextTask.fn(...nextTask.args)).then(val => {
this.inProcess = false;
nextTask.deferred.resolve(val);
this.check();
}).catch(err => {
console.log(err);
this.inProcess = false;
nextTask.deferred.reject(err);
this.check();
});
}
}
}
const Deferred = function() {
if (!(this instanceof Deferred)) {
return new Deferred();
}
const p = this.promise = new Promise((resolve, reject) => {
this.resolve = resolve;
this.reject = reject;
});
this.then = p.then.bind(p);
this.catch = p.catch.bind(p);
if (p.finally) {
this.finally = p.finally.bind(p);
}
}
let queue = new SerializedAsyncQueue();
// utility function
const sleep = function(t) {
return new Promise(resolve => {
setTimeout(resolve, t);
});
}
// only a single execution of this function is allowed at a time
// so it is run only via the queue that makes sure it is serialized
async function task(reason: string) {
function runIt() {
console.log("do thing because %s...", reason);
await sleep(1000);
console.log("done");
}
return queue.add(runIt);
}
// call task regularly
setIntervalAsync(async () => {
await task("ticker");
}, 5000) // normally 1min
// call task immediately
app.get("/task", async (req, res) => {
await task("trigger");
res.send("ok");
});
Here's a version using RxJS#Subject that is almost working. How to finish it depends on your use-case.
async function task(reason: string) {
console.log("do thing because %s...", reason);
await sleep(1000);
console.log("done");
}
const run = new Subject<string>();
const effect$ = run.pipe(
// Limit one task at a time
concatMap(task),
share()
);
const effectSub = effect$.subscribe();
interval(5000).subscribe(_ =>
run.next("ticker")
);
// call task immediately
app.get("/task", async (req, res) => {
effect$.pipe(
take(1)
).subscribe(_ =>
res.send("ok")
);
run.next("trigger");
});
The issue here is that res.send("ok") is linked to the effect$ streams next emission. This may not be the one generated by the run.next you're about to call.
There are many ways to fix this. For example, you can tag each emission with an ID and then wait for the corresponding emission before using res.send("ok").
There are better ways too if calls distinguish themselves naturally.
A Clunky ID Version
Generating an ID randomly is a bad idea, but it gets the general thrust across. You can generate unique IDs however you like. They can be integrated directly into the task somehow or can be kept 100% separate the way they are here (task itself has no knowledge that it's been assigned an ID before being run).
interface IdTask {
taskId: number,
reason: string
}
interface IdResponse {
taskId: number,
response: any
}
async function task(reason: string) {
console.log("do thing because %s...", reason);
await sleep(1000);
console.log("done");
}
const run = new Subject<IdTask>();
const effect$: Observable<IdResponse> = run.pipe(
// concatMap only allows one observable at a time to run
concatMap((eTask: IdTask) => from(task(eTask.reason)).pipe(
map((response:any) => ({
taskId: eTask.taskId,
response
})as IdResponse)
)),
share()
);
const effectSub = effect$.subscribe({
next: v => console.log("This is a shared task emission: ", v)
});
interval(5000).subscribe(num =>
run.next({
taskId: num,
reason: "ticker"
})
);
// call task immediately
app.get("/task", async (req, res) => {
const randomId = Math.random();
effect$.pipe(
filter(({taskId}) => taskId == randomId),
take(1)
).subscribe(_ =>
res.send("ok")
);
run.next({
taskId: randomId,
reason: "trigger"
});
});
function first(){
console.log('first')
}
function second(){
console.log('second')
}
let interval = async ()=>{
await setInterval(first,2000)
await setInterval(second,2000)
}
interval();
Imagine that I have this code above.
When I run it, first() and second() will be called at the same time; how do I call second() after first)() returns some data, for example, if first() is done, only then call second()?
Because first() in my code will be working with a big amount of data and if this 2 functions will be calling at the same time, it will be hard for the server.
How do I call second() each time when first() will return some data?
As mentioned above setInterval does not play well with promises if you do not stop it. In case you clear the interval you can use it like:
async function waitUntil(condition) {
return await new Promise(resolve => {
const interval = setInterval(() => {
if (condition) {
resolve('foo');
clearInterval(interval);
};
}, 1000);
});
}
Later you can use it like
const bar = waitUntil(someConditionHere)
You have a few problems:
Promises may only ever resolve once, setInterval() is meant to call the callback multiple times, Promises do not support this case well.
Neither setInterval(), nor the more appropriate setTimeout() return Promises, therefore, awaiting on them is pointless in this context.
You're looking for a function that returns a Promise which resolves after some times (using setTimeout(), probably, not setInterval()).
Luckily, creating such a function is rather trivial:
async function delay(ms) {
// return await for better async stack trace support in case of errors.
return await new Promise(resolve => setTimeout(resolve, ms));
}
With this new delay function, you can implement your desired flow:
function first(){
console.log('first')
}
function second(){
console.log('second')
}
let run = async ()=>{
await delay(2000);
first();
await delay(2000)
second();
}
run();
setInterval doesn't play well with promises because it triggers a callback multiple times, while promise resolves once.
It seems that it's setTimeout that fits the case. It should be promisified in order to be used with async..await:
async () => {
await new Promise(resolve => setTimeout(() => resolve(first()), 2000));
await new Promise(resolve => setTimeout(() => resolve(second()), 2000));
}
await expression causes async to pause until a Promise is settled
so you can directly get the promise's result without await
for me, I want to initiate Http request every 1s
let intervalid
async function testFunction() {
intervalid = setInterval(() => {
// I use axios like: axios.get('/user?ID=12345').then
new Promise(function(resolve, reject){
resolve('something')
}).then(res => {
if (condition) {
// do something
} else {
clearInterval(intervalid)
}
})
}, 1000)
}
// you can use this function like
testFunction()
// or stop the setInterval in any place by
clearInterval(intervalid)
You could use an IFFE. This way you could escape the issue of myInterval not accepting Promise as a return type.
There are cases where you need setInterval, because you want to call some function unknown amount of times with some interval in between.
When I faced this problem this turned out to be the most straight-forward solution for me. I hope it help someone :)
For me the use case was that I wanted to send logs to CloudWatch but try not to face the Throttle exception for sending more than 5 logs per second. So I needed to keep my logs and send them as a batch in an interval of 1 second. The solution I'm posting here is what I ended up using.
async function myAsyncFunc(): Promise<string> {
return new Promise<string>((resolve) => {
resolve("hello world");
});
}
function myInterval(): void {
setInterval(() => {
void (async () => {
await myAsyncFunc();
})();
}, 5_000);
}
// then call like so
myInterval();
Looked through all the answers but still didn't find the correct one that would work exactly how the OP is asked. This is what I used for the same purpose:
async function waitInterval(callback, ms) {
return new Promise(resolve => {
let iteration = 0;
const interval = setInterval(async () => {
if (await callback(iteration, interval)) {
resolve();
clearInterval(interval);
}
iteration++;
}, ms);
});
}
function first(i) {
console.log(`first: ${i}`);
// If the condition below is true the timer finishes
return i === 5;
}
function second(i) {
console.log(`second: ${i}`);
// If the condition below is true the timer finishes
return i === 5;
}
(async () => {
console.log('start');
await waitInterval(first, 1000);
await waitInterval(second, 1000);
console.log('finish');
})()
In my example, I also put interval iteration count and the timer itself, just in case the caller would need to do something with it. However, it's not necessary
In my case, I needed to iterate through a list of images, pausing in between each, and then a longer pause at the end before re-looping through.
I accomplished this by combining several techniques from above, calling my function recursively and awaiting a timeout.
If at any point another trigger changes my animationPaused:boolean, my recursive function will exit.
const loopThroughImages = async() => {
for (let i=0; i<numberOfImages; i++){
if (animationPaused) {
return;
}
this.updateImage(i);
await timeout(700);
}
await timeout(1000);
loopThroughImages();
}
loopThroughImages();
Async/await do not make the promises synchronous.
To my knowledge, it's just a different syntax for return Promise and .then().
Here i rewrote the async function and left both versions, so you can see what it really does and compare.
It's in fact a cascade of Promises.
// by the way no need for async there. the callback does not return a promise, so no need for await.
function waitInterval(callback, ms) {
return new Promise(resolve => {
let iteration = 0;
const interval = setInterval(async () => {
if (callback(iteration, interval)) {
resolve();
clearInterval(interval);
}
iteration++;
}, ms);
});
}
function first(i) {
console.log(`first: ${i}`);
// If the condition below is true the timer finishes
return i === 5;
}
function second(i) {
console.log(`second: ${i}`);
// If the condition below is true the timer finishes
return i === 5;
}
// async function with async/await, this code ...
(async () => {
console.log('start');
await waitInterval(first, 1000);
await waitInterval(second, 1000);
console.log('finish');
})() //... returns a pending Promise and ...
console.log('i do not wait');
// ... is kinda identical to this code.
// still asynchronous but return Promise statements with then cascade.
(() => {
console.log('start again');
return waitInterval(first, 1000).then(() => {
return waitInterval(second, 1000).then(() => {
console.log('finish again');
});
});
})(); // returns a pending Promise...
console.log('i do not wait either');
You can see the two async functions both execute at the same time.
So using promises around intervals here is not very useful, as it's still just intervals, and promises changes nothing, and make things confusing...
As the code is calling callbacks repeatedly into an interval, this is, i think, a cleaner way:
function first(i) {
console.log(`first: ${i}`);
// If the condition below is true the timer finishes
return i === 5;
}
function second(i) {
console.log(`second: ${i}`);
// If the condition below is true the timer finishes
return i === 5;
}
function executeThroughTime(...callbacks){
console.log('start');
let callbackIndex = 0; // to track current callback.
let timerIndex = 0; // index given to callbacks
let interval = setInterval(() =>{
if (callbacks[callbackIndex](timerIndex++)){ // callback return true when it finishes.
timerIndex = 0; // resets for next callback
if (++callbackIndex>=callbacks.length){ // if no next callback finish.
clearInterval(interval);
console.log('finish');
}
}
},1000)
}
executeThroughTime(first,second);
console.log('and i still do not wait ;)');
Also, this solution execute a callback every secondes.
if the callbacks are async requests that takes more than one sec to resolve, and i can't afford for them to overlap, then, instead of doing iterative call with repetitive interval, i would get the request resolution to call the next request (through a timer if i don't want to harass the server).
Here the "recursive" task is called lTask, does pretty much the same as before, except that, as i do not have an interval anymore, i need a new timer each iteration.
// slow internet request simulation. with a Promise, could be a callback.
function simulateAsync1(i) {
console.log(`first pending: ${i}`);
return new Promise((resolve) =>{
setTimeout(() => resolve('got that first big data'), Math.floor(Math.random()*1000)+ 1000);//simulate request that last between 1 and 2 sec.
}).then((result) =>{
console.log(`first solved: ${i} ->`, result);
return i==2;
});
}
// slow internet request simulation. with a Promise, could be a callback.
function simulateAsync2(i) {
console.log(`second pending: ${i}`);
return new Promise((resolve) =>{
setTimeout(() => resolve('got that second big data'), Math.floor(Math.random()*1000) + 1000);//simulate request that last between 1 and 2 sec.
}).then((result) =>{ // promise is resolved
console.log(`second solved: ${i} ->`,result);
return i==4; // return a promise
});
}
function executeThroughTime(...asyncCallbacks){
console.log('start');
let callbackIndex = 0;
let timerIndex = 0;
let lPreviousTime = Date.now();
let lTask = () => { // timeout callback.
asyncCallbacks[callbackIndex](timerIndex++).then((result) => { // the setTimeout for the next task is set when the promise is solved.
console.log('result',result)
if (result) { // current callback is done.
timerIndex = 0;
if (++callbackIndex>=asyncCallbacks.length){//are all callbacks done ?
console.log('finish');
return;// its over
}
}
console.log('time elapsed since previous call',Date.now() - lPreviousTime);
lPreviousTime = Date.now();
//console.log('"wait" 1 sec (but not realy)');
setTimeout(lTask,1000);//redo task after 1 sec.
//console.log('i do not wait');
});
}
lTask();// no need to set a timer for first call.
}
executeThroughTime(simulateAsync1,simulateAsync2);
console.log('i do not wait');
Next step would be to empty a fifo with the interval, and fill it with web request promises...
CODE -
.then (() => {
console.log("Wait");
setTimeout(function(){console.log("Wait to process")},1500);
this.timeout(2000);
})
.then(() => {
console.log("Get ABC");
return common.getApiData(url)})
Now when i run this code it logs data like -
Wait
Get ABC
Wait to process
(The it waits for the time specified above)
I want to put timeout before calling getApiData method..
Supposing you are using real promises, this is the function you should pass to then:
.then(function (value) {
var p = new Promise ();
setTimeout (function () {
p.resolve(value)
}, 2000)
return p
})
The next then will be called once the promise is resolved.