Why doesn't .then execute after previous .then in async function? - javascript

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();

Related

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

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.

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);

Does client-side code that calls an an async function need to use await?

Imagine the following hypothetical, minimal implementation of a function that does a simple HTTP GET request using axios. This uses await/async as depicted in the post.
const axios = require('axios')
exports.getStatus = async id => {
const resp = await axios.get(
`https://api.example.com/status/${id}`
)
return resp
}
Is the promise not resolved using await? Is it required that the client uses await as depicted below? Is it safe to assume that anytime a client consumes an async function, that it also needs to use await when calling the function?
// client
const { getStatus } = require('./status')
const response = await getStatus(4)
Short answer, no.
Labelling a function async means 2 things:
1) you're allowed to use await inside the function.
2) The function returns a promise; i.e. you can use await on its return value, or you can use .then, or Promise.all, or even ignore the return value, or anything else you can do with promises.
E.g. you could do the following which (depending on your use case) could be more performant because it needs not wait for the response to continue.
// client
const { getStatus } = require('./status')
const response4 = getStatus(4);
const response5 = getStatus(5);
// do stuff that don't rely on the responses.
response4.then(response => myOutput.setFirstOutput(response));
response5.then(response => myOutput.setSecondOutput(response));
Btw, your first snippet is redundant with its usage of await; it's equivalent to
const axios = require('axios')
exports.getStatus = id =>
axios.get(`https://api.example.com/status/${id}`);
This is because return await promise is equivalent to return promise, both return the same promise. There is one exception regarding rejected promises. Awaiting a rejected promise will throw an exception (which you can catch with a try/catch block), whereas directly returning a rejected promise will not throw an exception but you should still handle the rejection (with a .catch clause). To illustrate:
let promise = new Promise(resolve => setTimeout(() => resolve('resolved after 2 seconds'), 2000));
let returnPromise = () => promise;
let returnAwaitPromise = async () => await promise;
returnPromise().then(value => console.log('returnPromise,', value));
returnAwaitPromise().then(value => console.log('returnAwaitPromise,', value));
No, you don't need to use await. An async function returns a Promise that should be resolved. Instead of using await, you can simply call .then() like so:
// client
const { getStatus } = require('./status');
getStatus(4).then((response) => {
//Do something with the response
}).catch((error) => {
//Handle possible exceptions
});
Below I'll address your question from the comments. The getStatus function could be rewritten like this:
exports.getStatus = (id) => {
return new Promise((resolve, reject) => {
axios.get(`https://api.example.com/status/${id}`).then((resp) => {
resolve(resp);
}).catch((error) => {
reject(error);
})
});
}
It would act exactly the same as in your version. When you call asynchronous functions in series, you have to resolve each of the returned Promises. In your case, there are two of them - the one returned by axios.get and the one returned by getStatus.
No you don’t have to. The async function executes and waits until it returns error or completes execution and return a response!

Promise All retry

I know that promise.all() fails when even 1 of the promise is failed. I only want to try for failed promises and don't want to run promise.all() again.
Any recommendations on how I can achieve this in minimal way?
Promises are eager construct and model a value obtained asynchronously,
a Promise is produced using some kind of producer, like fetch for instance.
If you retain a reference to this producer then you can replay the nmechanism
that produced the Promise in the first place.
// producer function
function getData (arg) {
const result = new Promise();
return result.then(value => {
return { ok:true, value };
}, error => {
return {
ok: false,
value: error,
// retry is a function which calls the producer with the same arguments
retry: () => getData(arg)
};
})
}
Then if you have something like:
const data = [];
// Promise<{ok: boolean, value: any, retry?: function}>
// No promises will fail in this array
const asyncResults = data.map(getResults);
Promise.all(asyncResults)
.then((results) => {
const successes = results.filter(res => res.ok);
const retrys = results.filter(res => !res.ok).map(res => res.retry()); // retry all failed promises
})
Memory leaks, stack overflow: because I retain a reference to original arguments in order to retry and the algorithm is recursive there could be a memory leak. However the algorithm cannot "stack overflow":
getData calls do not get "deeper" over time (see retry definition)
the asyncrhonicity of the algorithm prevent this behaviour if a promise was never resolved
old data is properly discarded when accessing the results as const resultData = results.filter(res => res.ok).map(res => res.value);
However the algorithm could take a long time to settle if a promise keep on not getting resolved and prevent access to the rest of the values.
In an alternative I suggest you take a look at another async primitive, not yet part of the language (maybe some day) : Observables which are designed for this kind of tasks: lazy, retry-able async operations.
You may use async package and wrap all promise calls with closure with done argument.
Then simply resolve results.
const async = require('async');
const promiseAll = promises => {
return new Promise((resolve) => {
// preparing array of functions which has done method as callback
const parallelCalls = promises.map(promise => {
return done => {
promise
.then(result => done(null, result)
.catch(error => {
console.error(error.message);
done();
});
}
});
// calling array of functions in parallel
async.parallel(
parallelCalls,
(_, results) => resolve(results.filter(Boolean))
);
});
};
router.get('/something', async (req, res) => {
...
const results = await promiseAll(promises);
...
});
Or we can simply do Promise.all without using async package:
router.get('/something', async (req, res) => {
...
const results = (
await Promise.all(
promises.map(promise => {
return new Promise(resolve => {
promise.then(resolve).catch(e => resolve());
});
});
)
).filter(Boolean);
...
});

How can I make this JS code more functional using setTimeout and promises?

I'm trying to learn how to code using more functions and less loops and just in a more functional way. I want to implement a time out between calling connectBing. I was wondering if it's possible not to use the i variable and still get a 1 second time out between iterations. My code currently works but I'm looking for other ways to write it without using i.
This is my code:
// MAIN
getAllPosts().then((posts) => {
posts
.forEach( (post, i) => {
setTimeout(() => {
connectBing(anchorText,console.log).then()
} ,i * 1000)
})
// CONNECT TO BING WITH KW AND DO SOMETHING
function connectBing(anchorText,doSomethingWithBing) {
var deferred = q.defer();
request('https://www.cnn.com/search?q=' + anchorText, function (error, response, body) {
error ? console.log('error:', error) :
console.log('statusCode:', response && response.statusCode);
(doSomethingWithBing) ? doSomethingWithBing(body) : "You didn't give connectBing anything to do!"
})
return deferred.promise
}
You could take an array of async functions and chain each to run after the other. I will use native promises to demonstrate, and you can map it to the library you are using.
First create a function which takes in an array of async fucntions. It will chain one after the other, returning the last:
function chainAsyncFns(fns) {
// Ensure we have at least one promise to return
let promise = Promise.resolve();
fns.forEach(fn => promise = promise.then(fn));
return promise;
}
Then for each post, create an async function which will call connectBing and then wait for a timeout:
function connectBing() {
// Pretend we are connecting to a data source
return Promise.resolve();
}
function delay(ms) {
// Return a promise that resolves when the timeout is up
return new Promise(resolve => setTimeout(resolve, ms));
}
let fns = posts.map(post => () => {
return connectBing()
.then(() => delay(1000))
.catch(() => console.log('error'));
});
Chain the functions to run one after the other:
chainAsyncFns(fns).then(() => console.log('done'));

Categories

Resources