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()
}
})();
Related
I am trying to reduce an array of both asynchronous and synchronous methods. I have two synchronous methods and one asynchronous methods that does some basic formatting an object. Since I have a mix of asynchronous and synchronous methods, I am using async/await with my reduce. The problem is my formatName method returns the error Cannot read property 'toUpperCase' of undefined because the person parameter passed in is a promise. I thought that since i use await in my reduce, the callback should return the actual value instead of a promise:
const results = await fn(finalResult);
My code works fine if i take out the synchronous methods from my array. But I need to reduce an array of both synchronous and asynchronous methods. Does anyone have tips on how I can get around this? My full code is below.
const formatName = (person) => {
person.name = person.name.toUpperCase();
return person;
}
const formatAge = (person) => {
person.age += 10;
return person;
}
// Async function
const formatLocation = async (person) => {
await new Promise(resolve => setTimeout(resolve, 1000));
person.location = `${person.location.toLocaleLowerCase()}`
return person;
}
const initialPerson = {
name: "john",
age: 35,
location: 'USA'
}
const formattedStagesWithAsync = [
formatLocation, // asynchronous
formatAge, // synchronous
formatName //synchronous
];
const process = async () => {
const formattedPerson = await formattedStagesWithAsync.reduce(async (finalResult, fn) => {
const results = await fn(finalResult);
return results;
}, initialPerson);
console.log(`Formatted person - ${JSON.stringify(formattedPerson, null, 2)}`);
}
process();
Async functions always return promises, so on subsequent iterations of the .reduce callback, the accumulator will be a Promise that you need to wait to resolve first.
While you could do
const accumVal = await finalResult
at the beginning of the callback, this would be a lot simpler by avoiding reduce entirely IMO.
const process = async () => {
let person = initialPerson;
for (const fn of formattedStagesWithAsync) {
person = await fn(person);
}
In the case that each of your functions returns the original object, like in the case here, that simplifies to:
const process = async () => {
for (const fn of formattedStagesWithAsync) {
await fn(initialPerson);
}
(you're mutating the initialPerson in your original code too)
Promise return async value but dont use async and await option for Promise, Just return it
Promise docs
const promiseHandler = () => {
return new Promise((done, reject) => {
setTimeout(() => {
done("hello");
});
});
}
promiseHandler().then(v => {
console.log(v); // hello
});
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.
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);
Multiple calls to _dispatch sometimes causes the promises passed to _dispatch to be executed at the same time. Isn't .then supposed to execute after previous .then?
// Failing code
async _dispatch (promise) {
// this._mutex is a Promise
this._mutex = this._mutex.then(() => promise)
return Promise.resolve(this._mutex)
}
// Possibly working code
async _dispatch (promise) {
console.log('START_CS', promise)
while (Atomics.load(this.done, 0) === 0) {
await this.sleep(50)
}
Atomics.store(this.done, 0, 0)
console.log('IN_CS', promise)
const ret = await promise
Atomics.store(this.done, 0, 1)
console.log('END_CS', promise)
return ret
}
_dispatch is used in the following manner:
async getStatus (ports) {
const request = // ...
return this._dispatch(someAsyncFunctionReturningArray(request, ports))
}
const polling = () => {
const sleep = new Promise(resolve => setTimeout(resolve, 500))
const status = this.getStatus().then(() => {}).catch(() => {})
return Promise.all([sleep, status])
.then(polling)
}
polling()
polling() and another similar block of code is running at the same time. I noticed that someAsyncFunctionReturningArray is called concurrently.
Promises carry information about the state of a task and allow you to act on that state. They don’t, in general, represent tasks themselves. Here’s the equivalent of what you’re doing:
async function foo() {
console.log('foo() task ran');
}
function delay() {
return new Promise(resolve => {
setTimeout(resolve, 1000);
});
}
const promise = foo();
delay().then(() => promise)
This isn’t going to delay promise by a second, because promise is just an object that can say “resolved with value X”, “rejected with error Y”, or “pending”. There’s no concept of delaying promises – you delay tasks. The work is done by foo and starts when you call foo().
It’s not quite clear what the correct replacement would be in your question. I think this is what you were going for:
_dispatch (action) {
this._mutex = this._mutex.then(() => action())
return this._mutex
}
async getStatus (ports) {
const request = // ...
return this._dispatch(() => someAsyncFunctionReturningArray(request, ports))
}
but it’s possible there’s an entirely different approach that works better, and we’d need more details on what you’re trying to accomplish with this queue to recommend one.
A Promise is not a task that generates a value, it is rather a value immeadiately returned by tasks that take a while, to then somewhen pass out the result. Adding a promise to another promise through chaining them does not influence what the tasks are doing. However a callback could be called when a promise resolves, like:
async _dispatch(callback) {
this._mutex = this._mutex.then(() => callback());
return this._mutex;
}
That can then be used as:
const getStatus = (ports) => this.dispatch(() => {
const request = // ...
return someAsyncFunctionReturningArray(request, ports);
});
const sleep = new Promise(resolve => setTimeout(resolve, 500))
const polling = () => {
const status = this.getStatus().then(() => {}).catch(() => {})
return Promise.all([sleep, status])
.then(polling)
};
polling();
With Promises I can have two separate "threads" both waiting for the same value:
let trigger;
const promise = new Promise(r => {
console.log('promise is created *once*');
trigger = value => {
console.log('trigger is called *once*');
r(value);
}
});
(async () => {
console.log('A waiting');
const value = await promise;
console.log(`A finished, got ${value}`);
})();
(async () => {
console.log('B waiting');
const value = await promise;
console.log(`B finished, got ${value}`);
})();
trigger('hello');
console.log('And *two* things are waiting on the single promise');
I've tried to replicate this with async/await but to no avail.
The following snippet does not work:
let trigger = async () => {
console.log('trigger should be called *once*');
return 'hello';
};
(async () => {
console.log('A waiting');
const value = await trigger; // <-- What do I need to put here?
console.log(`A finished, got ${value}`);
})();
(async () => {
console.log('B waiting');
const value = await trigger; // <-- What do I need to put here?
console.log(`B finished, got ${value}`);
})();
trigger(); // <-- How can this "kick off" the two awaits above?
Is it possible to write the same functionality in the first snippet, using async/await?
I'm okay with falling back to using Promise if that's needed.
To await, you need to have a reference to the singular promise, so you can't call a function on demand and have that function create a promise and then use that same promise elsewhere (unless the function that creates the promise also holds it in state to return to other callers, like a singleton).
I'd create a singular promise initially, and then send it to the async functions:
const trigger = async () => {
console.log('trigger should be called *once*');
return 'hello';
};
async function as1(prom) {
console.log('A waiting');
const value = await prom;
console.log(`A finished, got ${value}`);
}
async function as2(prom) {
console.log('B waiting');
const value = await prom;
console.log(`B finished, got ${value}`);
}
const thePromise = trigger();
as1(thePromise);
as2(thePromise);
Don't use async just to return a promise, though - if the purpose of a function is to create a promise, then do it explicitly - that way, it's more clear what your code is meant to do. Async and await have not made the Promise keyword obsolete, it's just syntax sugar which is useful in some situations (and unnecessary in others).