async await, process one item of For loop at a time - javascript

I can't seem to wrap my head around async await, let's say I want to process one item of an array at a time, with a 1 second delay:
myfunction() {
clearTimeout(this.timer);
this.timer = setTimeout(() => {
for (const item of this.myarray) {
this.dosomething(item).then((res: string) => {
setTimeout(() => {
await this.final_thing_before_the_next_item.push(res);
}, 1000);
});
}
}, 200);
}
Where would I put the "async"?

Stackblitz demo
You can put an async inside the setTimeout:
myfunction() {
setTimeout(async () => {
for (const item of this.myarray) {
await this.wait(1000);
console.log(item);
}
});
}
wait(timer: number): Promise<void> {
return new Promise((resolve, reject) => {
setTimeout(() => resolve(), timer);
});
}

In a simplified form, await is syntax sugar for consuming promises. The following snippets are pretty much equivalent:
doSomethingAsync().then(value => {
console.log('it finished', value);
});
const value = await doSomethingAsync();
The caveat is that if you want to use await, you need to mark the function as async. I will assume you want to have a 1s delay after full completion. Your code might look something like this:
function wait() {
return new Promise(resolve => {
setTimeout(resolve, ms);
});
}
async function processItems(items) {
for (const item of items) {
const res = await this.doSomething(item);
// This isn't processed until doSomething completes.
const other = await this.doFinalThing(res);
// This isn't run until doFinalThing completes.
await wait(1000);
// The loop now continues after waiting 1s.
}
}

you marked this as angular, so I'm going to say, consider rxjs...
// imports
import {timer, from, concat} from 'rxjs'
import {switchMap, delay} from 'rxjs/operators'
// wait 200 ms
timer(200).pipe(
// switch
switchMap(() => {
// map array into streams
const obs$ = this.myArray.map(i => {
// from promise, do whatever
return from(this.dosomething(i)).pipe(
// switch again, to do final thing, again from promise
switchMap(res => from(this.finalThing(res))),
// delay 1s
delay(1000)
)
});
return concat(...obs$); // execute one after the other
})
).subscribe(v => console.log('values 1 by 1, spaced 1s apart', v))

If you want to process one asynchronous operation at a time, you could do the following:
// "wait" for a 1000ms before resolving the Promise
const wait = (ms = 1000) => {
return new Promise(resolve => {
setTimeout(() => resolve(), ms);
});
};
// array of asynchronous operations
const promises = Array.from({ length: 4 }).map((_, idx) =>
Promise.resolve(idx)
);
async function main() {
const data = [];
for (let item of promises) {
const result = await item;
await wait(1000);
data.push(result);
console.log("result", result);
}
console.log("final data", data);
}
main();

If I understand your question (and code sample) correctly, you basically want to
Sleep 200 ms
Iterate over a list of items. For each item you want to:
Call a function, passing the current item to it and getting a response in return.
Pause for 1 second.
Call a second function, passing it the response
To do that you need a sleep() function that returns a promise that will resolve when the specified time has elapsed:
function sleep(ms = 1000) {
const p = new Promise( (resolve, reject) => {
setTimeout(() => resolve());
});
return p;
}
Having that, you need an async function to do the actual work. It has to be async because that is what lets it await other things:
async function process_items( items, delayInMs = 1000 ) {
for ( item of items ) {
const res = await doSomething( item, delayInMs );
await sleep(delay);
await doSomethingElse( res );
}
}
As you can see from the above bit of code, the advantage of using async/await over callbacks or promise chains is that it gives you a rather more succinct and declarative syntax.
Then you can wrap it all up in another async function:
async function myfunction() {
await sleep(200);
await process_items( this.myarray, 1000 );
}
Marking a function as async does two things: it
Enables the use of await within that function, and
Converts the function into a function returning a promise, regardless of what its ostensible return value is.
If you take this function:
function foo() {
return 1;
}
and mark it as async:
async function foo() {
return 1;
}
it is (more or less) as if you had changed to to read:
function foo() {
return Promise.resolve(1);
}

The easiest way, without looking at the specifics of your code is before myFunction ie async myFunction(). I see 'this' being used so I imagine this is a method of some object/class in which case that would be one way to go about it.

Related

For-await loop inside Promise

Imagine we have an async generator function:
async f * (connection) {
while (true) {
...
await doStuff()
yield value
}
}
Suppose that this function is virtually endless and gives us results of some async actions. We want to iterate these results:
for await (const result of f(connection)) {
...
}
Now imagine we want to break out of this for-await loop when some timeout ends and clean things up:
async outerFunc() {
setTimeout(() => connection.destroy(), TIMEOUT_MS)
for await (const result of f(connection)) {
...
if (something) {
return 'end naturally'
}
}
}
Assume that connection.destroy() ends the execution of f and ends the for-await loop. Now it would be great to return some value from the outerFunc when we end by timeout. The first thought is wrapping in a Promise:
async outerFunc() {
return await new Promise((resolve, reject) => {
setTimeout(() => {
connection.destroy()
resolve('end by timeout')
}, TIMEOUT_MS)
for await (const result of f(connection)) { // nope
...
if (something) {
resolve('end naturally')
}
}
})
}
But we cannot use awaits inside Promise and we cannot make the function async due to this antipattern
The question is: how do we return by timeout the right way?
It gets much easier, if you use an existing library that can handle asynchronous generators and timeouts automatically. The example below is using library iter-ops for that:
import {pipe, timeout} from 'iter-ops';
// test async endless generator:
async function* gen() {
let count = 0;
while (true) {
yield count++; // endless increment generator
}
}
const i = pipe(
gen(), // your generator
timeout(5, () => {
// 5ms has timed out, do disconnect or whatever
})
); //=> AsyncIterable<number>
// test:
(async function () {
for await(const a of i) {
console.log(a); // display result
}
})();
Assume that connection.destroy() ends the execution of f and ends the for-await loop.
In that case, just place your return statement so that it is executed when the loop ends:
async outerFunc() {
setTimeout(() => {
connection.destroy()
}, TIMEOUT_MS)
for await (const result of f(connection)) {
...
if (something) {
return 'end naturally'
}
}
return 'end by timeout'
}

Difference between returning 'fetch(...)' vs returning 'fetch(..).then(...)' [duplicate]

I'm trying to understand difference between these 3. Callbacks & Promises are clear but I don't get the usage of async/await. I know it is the syntactic sugar of promises but what I've tried didn't work. I'm sharing the piece of code I've tried to understand all this...
I've tried with an array
var array = [1,2,3];
and 2 functions
get() executes in 1 sec & console the array
post(item) executes in 2 sec & push a new item in the array
Now, what I want to get is that the post method should execute first & get after it so that the result on the console should be [1,2,3,4] not [1,2,3]
CALLBACK
function get() {
setTimeout(() => console.log(array), 1000);
}
function post(item, callback) {
setTimeout(() => {
array.push(item);
callback();
}, 2000);
}
function init() {
post(4, get);
// returns [1,2,3,4] ✅
}
It works fine but in case of too many callbacks, code will be messier... So,
PROMISE
function get() {
setTimeout(() => console.log(array), 1000);
}
function post(item) {
return new Promise((resolve, reject) => setTimeout(() => {
array.push(item)
resolve();
}, 2000));
}
function init() {
post(4).then(get);
// returns [1,2,3,4] ✅
}
Ok with much cleaner code. But still multiple then calls... Now,
Async/Await
function get() {
setTimeout(() => console.log(array), 1000);
}
function post(item) {
setTimeout(() => {
array.push(item)
}, 2000);
}
async function init() {
await post(4);
get();
// returns [1,2,3] ❌
await post(4);
await get();
// returns [1,2,3] ❌
post(4);
await get();
// returns [1,2,3] ❌
}
Much more cleaner version but neither way, it worked... I've also tried this (convert both functions (post & get) to async and call with then)
async function get() {
setTimeout(() => console.log(array), 1000);
}
async function post(item) {
setTimeout(() => {
array.push(item)
}, 2000);
}
async function init() {
post(4).then(get);
// returns [1,2,3] ❌
}
But still of no use. So I'm totally confused about this feature (i.e. async/await). Please elaborate on this very example. And also please tell me about Promise.resolve & Promise.all in this same context! Thanks
async and await are tools to manage promises
await post(4);
Here you are waiting for the promise returned by post to be resolved.
function post(item) {
setTimeout(() => {
array.push(item)
}, 2000);
}
However, post does not return a promise, so it does nothing useful.
You had a working implementation of post with a promise before. So use that:
function post(item) {
return new Promise((resolve, reject) => setTimeout(() => {
array.push(item)
resolve();
}, 2000));
}
Your attempts with async and await do not use anything that resolves a promise after a delay. As it stands, all the async functions you have defined, return a promise that immediately resolves.
You'll want to use a utility function that you can often use. It returns a promise that resolves after a given number of milliseconds, using setTimeout:
// utility function
let delay = ms => new Promise(resolve => setTimeout(resolve, ms));
let array = [1, 2, 3];
async function get() {
await delay(1000);
console.log(array);
}
async function post(item) {
await delay(1000);
array.push(item)
}
async function init() {
await post(4);
await get();
}
init();
console.log("wait for it...");
Similar to what you did in PROMISE.
Wrap post setTimeout in a promise and return it.
call resolve inside the body of the settimeout.
function post(item) {
return new Promise((resolve)=>{
setTimeout(() => {
array.push(item)
resolve()
}, 2000);
})
}
then you can use it like that :
async function init() {
await post(4);
get();
}

How to use promise in order to wait for a function to execute

I have an async piece of javascript code that need to execute and then the next statements can be executed
function getEventData() {
return new Promise(resolve => {
eventList.forEach((event) => {
db.collection('Events').doc(event)?.collection('registeredStudents')?.get()
.then((querySnapshot) => {
eventData.push({"id": event, "number": querySnapshot.size})
})
})
resolve();
})
}
getEventData().then(console.log(eventData))
eventList is an array with 17 elements and it list through the database around 17 times, that is inefficient but I had no choice so had to do like that. Anyways in the console I get an empty array logged. How do I solve that. PS: I am new to async javascript and promises.
You can use Promise.all():
function getEventData() {
return new Promise(async (resolve) => {
await Promise.all(
eventList.map(async (event) => {
let querySnapshot = await db.collection('Events').doc(event)?.collection('registeredStudents')?.get()
eventData.push({"id": event, "number": querySnapshot.size})
})
);
resolve();
})
}
getEventData().then(console.log(eventData))
This is a simple demo of how to use promises in a loop. The async operation I am performing is "sleeping" (aka nothing happens until everything has "slept")..
const sleepTimes = [1, 2, 3];
const promises = [];
sleepTimes.forEach(time => {
const promise = new Promise((resolve, reject) => {
return setTimeout(resolve, time * 1000, time * 1000);
});
promises.push(promise);
});
console.log(promises);
Promise.all(promises).then((values) => {
document.body.innerHTML = values;
});
Which would mean your function should look something like:
function getEventData() {
const promises = [];
eventList.forEach((event) => {
// I assume this already returns a promise, so there's no need to wrap it in one
const promise = db.collection("Events").doc(event)?.collection("registeredStudents")?.get();
promises.push(promise);
});
return Promise.all(promises);
}
getEventData().then((values) => console.log(values));
The correct way to deal with your problem is not using new Promise with resolve, yet by using async await.
When you await a promise, it waits for it to resolve and returns the result (not a promise!) before continuing to the next line.
A function awaiting a promise returns a promise when called.
Promise.all combines multiple promises to one, so you can wait for multiple promises to resolve all at once, and have them run at the same time (async).
async function getEventData(eventList) {
try {
const eventData = [];
await Promise.all(eventList.map(async event => {
const querySnapshot = await db.collection('Events').doc(event)?.collection('registeredStudents')?.get();
eventData.push({"id": event, "number": querySnapshot.size});
}));
console.log(eventData)
} catch (exception) {
// error handling
}
}

How to execute asynchronous tasks in order

I'm currently experimenting with asynchronous functions in Javascript and I stumbled upon a case where I wanted to execute a collection of asynchronous functions in the order that they are placed in an array.
I also want the possibility to pass an argument to an asynchronous function or not. With my current solution, the two functions with a passed argument are executed first and at the same time, then the other two functions are executed one after the other.
const asyncOne = async (value = "no value passed to async 1") => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(value);
resolve();
}, 1000);
});
};
const asyncTwo = async (value = "no value passed to async 2") => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(value);
resolve();
}, 1000);
});
};
const sequence = async tasks => {
const resolve = Promise.resolve(null);
await tasks.reduce(
(promise, task) =>
promise.then(() => (typeof task === "function" ? task() : task)),
resolve
);
};
(async () => {
const res = await sequence([asyncOne("first"), asyncTwo, asyncOne, asyncTwo("last")]);
})();
Expected output:
"first"
"no value passed to async 2"
"no value passed to async 1"
"last"
Actual output:
"first"
"last"
"no value passed to async 2"
"no value passed to async 1"
As I mentioned in my comment, you should not mix new Promise with async – there's no need to, and you'll just confuse yourself and any future readers.
Either way, an async function will begin to execute synchronously, which is why you see your sequence (both the first and last timeouts begin at the same moment). To fix this, you'd need to use a function that calls the function that returns a promise, like so:
function delay(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
async function asyncOne(value = "async1 default") {
await delay(1000);
console.log(value);
}
async function asyncTwo(value = "async2 default") {
await delay(1000);
console.log(value);
}
async function sequence(tasks, init = undefined) {
let value = init;
for (let task of tasks) {
if (typeof task === "function") value = await task(value);
else value = await task;
}
return value;
}
(async () => {
await sequence([
() => asyncOne("first"),
asyncTwo,
asyncOne,
asyncTwo.bind(this, "last"), // can also use `bind` instead of an anonymous arrow function
]);
})();
This implementation also lets you chain values from one promise to another, and allows for an initial value to be passed to the first promise-returning function, á la
await sequence([asyncTwo], "hello!");
asyncOne("first") and asyncTwo("last") call the functions and execute them immediately, so it prevents you from executing them in order.
Instead, write asyncOne.bind(this, "first"), which does not execute the function immediately and allows you to execute it later.
(async () => {
const functions = [
asyncOne.bind(this,"first"),
asyncTwo,
asyncOne,
asyncTwo.bind(this, "last")
];
for(let f of functions){
await f()
}
})();

Promise.all Replacement?

Is there any way where I can call multiple async functions in Javascript, but get response of the call as soon as one completes, unlike Promise.all that waits for all async calls to complete?
What I want is, run async calls in parallel, and get response of a respective call as soon as it gets completed while other calls are still running and then update the react state.
I am already using Promise.all for multiple async calls, but it gives responses when all calls are finished.
You can just iterate over an array of promises and change the state on each promise resolution:
let state = null;
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve({p1: 1});
}, 2000);
});
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve({p2: 2});
}, 1000);
});
const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve({p3: 3});
}, 3000);
});
// Iterate promises
[p1, p2, p3].forEach(p => {
p.then(res => {
state = Object.assign({}, state, res);
console.log(state);
});
});
console.log(state);
You could create your own Promise method for this task:
if (!Promise.each) {
Promise.each = function(promises, callback, error) {
if (promises && Array.isArray(promises)) {
promises.forEach(p => {
p.then(res => {
callback(res);
}, err => {
if (error) {
error(err);
} else {
console.log(err);
}
});
});
}
}
}
// Usage
Promise.each([p1, p2, p3], updateState);
In your question the phrase "than update ... state" is the key. You plan to update the state in such a way that as soon as one promise "completed" the corresponding state is updated. You have been trying something like this
async function f1(){ await Promise.resolve(); }
async funciton f2(){ await Promise.resolve(); }
async function fn(){ await Promise.resolve(); }
async function drvAsync(){
const [r1, r2, rn] = await Promise.all([f1(), f2(), fn()]);
u1(r1);
u2(r2);
un(rn);
}
where f[n] is an async business function, u[n] is a method to deal with the result from it. This schema is not acceptable in your scenario. Perhaps fn completes faster than others and you want to update N-th state earlier.
My recommendation is use no synchronization primitives at all. Instead you should deal with the results separately.
function drv(){
f1().then((r1)=>u1(r1)).catch((e)=>er(e));
f2().then((r2)=>u2(r2)).catch((e)=>er(e));
fn().then((rn)=>un(rn)).catch((e)=>er(e));;
}
This schema will call each update (u[n]) method without waiting results from others.
This is what Promise.race is for:
The Promise.race() method returns a promise that fulfills or rejects as soon as one of the promises in an iterable fulfills or rejects, with the value or reason from that promise.
For instance:
function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
async function main() {
const promises = [
sleep(1000).then(() => "world"),
sleep(100).then(() => "hello"),
];
const promisesWithIndices = promises.map((p, index) =>
p.then((result) => ({result, index}))
);
while (promisesWithIndices.length > 0) {
const {result, index} = await Promise.race(promisesWithIndices);
console.log("%s -> %s", index, result);
promisesWithIndices.splice(index, 1);
}
}
main(); // prints "1 -> hello"; then prints "0 -> world" after a while
As long as you don't use async/await you can do the calls in parallel. Note that browsers have connection limit for network requests so if you execute for example 100 calls it will not be parallel but rather be queued.
You need a callback to execute when the promise resolved. If this callback is the same for all functions than just loop all calls, create the promise and resolve with the same function.
function resolved(data) {
// ...
}
const promise1 = new Promise(function(resolve, reject) {
resolve('Success!');
}).then(resolved);
const promise2 = new Promise(function(resolve, reject) {
resolve('Success!');
}).then(resolved);

Categories

Resources