nodejs - Chaining dependent asynchrous operations - javascript

I have two asynchronous operations such that the invocation of the second one uses input from the output of the first. To implement such invocations with async and await, it seems, it won't turn out to be too different from callbacks.
Consider this.
async function loop(label, I) {
console.log('%s - Looping for %d times.', label, I)
console.time(label)
for (i = 0; i < I; ++i) {
}
return Promise.resolve(I)
}
//
async function demo0() {
// Refer - https://stackoverflow.com/a/45479579/919480
let I0 = loop('01', 10)
let I1 = loop('02', I0 * 1000)
await I0
await I1
}
//
async function demo1() {
// Refer - https://stackoverflow.com/a/45479579/919480
let I0 = loop('11', 10)
await I0
let I1 = loop('12', I0 * 1000)
await I1
}
//
async function demo2() {
await loop('21', 10).then(async (i) => {
await loop('22', i * 1000)
})
}
//
(async () => {
await demo0()
await demo1()
await demo2()
})()
Result:
01 - Looping for 10 times.
02 - Looping for NaN times.
11 - Looping for 10 times.
12 - Looping for NaN times.
21 - Looping for 10 times.
22 - Looping for 10000 times.
The second loop should iterate based on a value passed on by the first loop. In demo0 and demo1, the second loop receives a NaN because they are triggered simultaneously. Only in demo2, does the second loop receive the correct value. One could have achieved this behavior with callbacks too.
Is there a async/await way to achieve this behavior?

Every call of an async function gives you a Promise in return, but Promises can't be (sensibly) added to numbers, so you get NaN in return.
If you want to be able to use the result of a Promise, you should await it and use the resulting expression. If you await the Promise and then try to use the original Promise, you'll still have a Promise, not a value, so you should assign the awaited Promise to a variable, eg change
let I0 = loop('01', 10)
let I1 = loop('02', I0 * 1000)
await I0
await I1
to
let I0 = loop('01', 10)
const resolveValueI0 = await I0;
let I1 = loop('02', resolveValueI0 * 1000)
await I1
(you cannot call the second loop until I0 is done, because the second loop needs the number from the resolution of I0's Promise. Either that, or pass the Promise to I1, and have I1 properly consume it with .then or await)
and
let I0 = loop('11', 10)
await I0
let I1 = loop('12', I0 * 1000)
await I1
to
let I0 = loop('11', 10)
const resolveValueI0 = await I0;
let I1 = loop('12', resolveValueI0 * 1000)
await I1
async function loop(label, I) {
console.log('%s - Looping for %d times.', label, I)
console.time(label)
for (i = 0; i < I; ++i) {
}
return Promise.resolve(I)
}
//
async function demo0() {
// Refer - https://stackoverflow.com/a/45479579/919480
let I0 = await loop('01', 10)
let I1 = loop('02', I0 * 1000)
await I0
await I1
}
//
async function demo1() {
// Refer - https://stackoverflow.com/a/45479579/919480
let I0 = loop('11', 10)
const result = await I0
let I1 = loop('12', result * 1000)
await I1
}
//
async function demo2() {
await loop('21', 10).then(async (i) => {
await loop('22', i * 1000)
})
}
//
(async () => {
await demo0()
await demo1()
await demo2()
})()

Related

JavaScript: async function that would retry for a period of time at a given interval

I am trying to write an async function that is going to await a function that got passed as one parameter and I wanted this async function to retry this operation for 5 mins every 10 seconds.
I found one function that sort of does this but it retries based on the numbers of times instead.
async function retry(fn, n) {
for (let i = 0; i < n; i++) {
try {
const ret = await fn();
if(!ret) throw new Error() // if `ret` is null or undefined, we will retry.
return ret
} catch {}
}
throw new Error(`Failed retrying ${n} times`);
}
Is there a way to tweak this function to satisfy my use cases?
Since your function is async, you can easily create timeouts to wait between subsequent calls:
const sleep = t => new Promise(r => setTimeout(r, t))
async function retry(fn) {
const startTime = Date.now()
while(Date.now() - startTime < 5 * 60 * 1000) {
try {
const ret = await fn();
if(ret)
return ret
// if `ret` is null or undefined, we won't return.
} catch {}
await sleep(10 * 1000)
}
throw new Error(`Failed retrying`);
}

how to delay execution inside of return new promise

I'm working on a web-based visualization tool of algorithms using javascript, so it needs some pause during the execution. To pause execution I used const delay = ms => new Promise(res => setTimeout(res, ms)); and called function await delay(500);, which I found on stack overflow, it worked fine with a non-recursive algorithm. And to make recursive algorithm sequential I used return new Promise((resolve,reject) and .then(). but now I can't use await inside of return new Promise, what is the reason, and am I on right track?
my code looks like this(quicksort)
const quick = async(low, high) =>
{
return new Promise((resolve,reject) =>
{
if (low < high)
{
var pivot = color[high]
var i = low -1
for(var j = low; j <= high - 1; j++)
{
// I want some delay here
if(color[j] < pivot)
{
i++
swap(i,j)
// delay here
}
}
var pi = i+1;
swap(pi,high)
// and delay here
quick( low, pi - 1).then(quick( pi + 1, high));
}
resolve();
});
}
You don't need return new Promise(... because async functions already return promises. You can't use await because the promise handler is not an async function. Try this:
const quick = async (low, high) => {
if (low < high) {
var pivot = color[high];
var i = low - 1;
for (var j = low; j <= high - 1; j++) {
await delay(500);
if (color[j] < pivot) {
i++;
swap(i, j);
// delay here
}
}
var pi = i + 1;
swap(pi, high);
await delay(500);
await quick(low, pi - 1);
await quick(pi + 1, high);
}
// return is implicit
};

JS - Why does await not work in class

I need to wait some time inside a function of a class. I tried to adapt Jonas W. answer: https://jsfiddle.net/5wk2cohe/
const sleep = ms => new Promise(res => setTimeout(res, ms));
class K {
run(n) {
(async function() {
document.body.textContent = n;
await sleep(1000);
})();
}
}
var v = new K();
for (let n = 0; n < 4; n++) {
v.run(n);
}
But the counts prompts immediately 3. If I run https://jsfiddle.net/tctxcn9o/
I see a counter as excepted:
const sleep = ms => new Promise(res => setTimeout(res, ms));
(async function() {
for(let n = 0; n < 4; n++) {
document.body.textContent = n;
await sleep(1000);
}
})();
What I am doing wrong?
[Update] Some background: In my hometown a very old programm called JavaKara is very popular to help students at the high school to lern how to code:
To see how the ladybug moves is very helpful for understanding and finding your error. So I am trying to wait some time after each step of the ladybug.
As El Aoutar Hamza said, you're creating 4 async tasks where every task is run instantly. In order to delay the next task, you need to use await inside the loop and make the run function return a Promise, something like this:
const sleep = ms => new Promise(res => setTimeout(res, ms));
class K {
async run(n) {
document.body.textContent = n;
await sleep(1000);
}
}
(async function() {
var v = new K();
for (let n = 0; n < 4; n++) {
await v.run(n);
}
})();
In your first example, you are creating 4 async functions that are not related to each other, so none of them has to wait for the other.
While in your second example, you are creating 1 async function, where the await expression causes the async function execution to pause until the promise sleep is fulfilled.

Javascript, calling asynchronous function in a for loop [duplicate]

This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 5 years ago.
I wish to call an asynchronous function in a for loop. I am having significant trouble doing so and am getting a variety of errors such as undefined variables and such.
Evaluator.prototype.asyncEval = function(predictor) {
let self = this;
let metric = 0; //METRICS SHOULD BE UPDATED BY ASYNC FUNCTION
for (let i = 1; i < this.fullTraces.length; i++) {
(function(index){
let deltaTime = self.fullTraces[i][2] - this.fullTraces[i-1][2];
let subTraces = self.fullTraces.slice(0, i);
predictor.predict(subTraces, (dist) => { // ASYNC FUNCTION
if (dist !== null) {
let result = dist.getTopK(1);
let pX = result[0][0][0];
let pY = result[0][0][1];
let x = self.fullTraces[i][0];
let y = self.fullTraces[i][1];
let a = pX - x;
let b = pY - y;
metric += Math.sqrt(a*a + b*b);
}
});
}(i));
}
metric /= this.fullTraces.length - 1;
return metric;
}
My asynchronous function predictor.predict() is actually using a POST request to get results from my web server.
YourPredictor.prototype.predict = function(trace, callback) {
return asyncPostRequest('https://0.0.0.0:5000/prediction', trace, responseText => {
prediction = JSON.parse(responseText);
let pred = [prediction['xs'], prediction['ys'], 'm'];
let dist = Dist.NaiveDistribution.from(pred, mouseToKey);
dist.set(pred, 1);
callback(dist);
});
}
How can I get this to work? I am running this on Chrome. I know there is the new await and async from ES7, but I don't want to use something that bleeding edge yet.
You need to refactor the code to replace the loop with a self-invoking loop, so that each time the asynchronousis called, the result from it is handed back, then iteration is checked, if i
Since the main code is asynchronous you will also need a callback for the initial call function (the doneCallback below).
Example
I left in the original code where it is expected to work, but made a couple of changes for it to work here.
function Evaluator() {}; // dummy for test
Evaluator.prototype.asyncEval = function(predictor, doneCallback) {
let self = this;
let metric = 0;
let length = 10; //this.fullTraces.length;
let i = 1;
// replaces for-loop
(function loop() {
self.predict(0, (dist) => {
// ...
metric += dist;
if (++i < length) loop();
else {
// ...
doneCallback(metric);
}
});
})();
}
// note: I changed prototype parent in this example
Evaluator.prototype.predict = function(trace, callback) {
//...
setTimeout(callback, 100, Math.random() * 100); // simulate async call
}
// TEST
var test = new Evaluator();
test.asyncEval(0, function(result) {
document.querySelector("div").innerHTML = result;
});
<div>Calcing...</div>
Example leaving the original code in place at the intended locations:
function Evaluator() {}; // dummy for test
Evaluator.prototype.asyncEval = function(predictor, doneCallback) {
let self = this;
let metric = 0; //METRICS SHOULD BE UPDATED BY ASYNC FUNCTION
let length = 10; //this.fullTraces.length;
let i = 1;
// replaces for-loop
(function loop() {
//let deltaTime = self.fullTraces[i][2] - this.fullTraces[i - 1][2];
//let subTraces = self.fullTraces.slice(0, i);
self.predict(0, (dist) => { // ASYNC FUNCTION
//predictor.predict(subTraces, (dist) => { // ASYNC FUNCTION
/*if (dist !== null) {
let result = dist.getTopK(1);
let pX = result[0][0][0];
let pY = result[0][0][1];
let x = self.fullTraces[i][0];
let y = self.fullTraces[i][1];
let a = pX - x;
let b = pY - y;
metric += Math.sqrt(a * a + b * b);
}*/
metric += dist;
if (++i < length) loop();
else {
//metric /= this.fullTraces.length - 1;
//return metric; <- don't use, instead use:
doneCallback(metric);
}
});
})();
}
// note: I changed prototype parent in this example
Evaluator.prototype.predict = function(trace, callback) {
setTimeout(callback, 100, Math.random() * 100); // simulate async call
/*return asyncPostRequest('https://0.0.0.0:5000/prediction', trace, responseText => {
prediction = JSON.parse(responseText);
let pred = [prediction['xs'], prediction['ys'], 'm'];
let dist = Dist.NaiveDistribution.from(pred, mouseToKey);
dist.set(pred, 1);
callback(dist);
});*/
}
// TEST
var test = new Evaluator();
test.asyncEval(0, function(result) {
document.querySelector("div").innerHTML = result;
});
<div>Calcing...</div>
If you dont want to use async + await combination, I would suggest to take a look at this post.
Asynchronous for cycle in JavaScript
I'm using this asyncLoop function and it's working great:
The function takes three arguments: 1) iterations, 2) a loop callback function and 3) Done callback function,
Check out the code:
function promise1(param){
return new Promise((resolve, reject) => setTimeout(() => { resolve(`Promise1 ${param} Done`)}, 2000))
}
function asyncLoop(iterations, func, callback) {
var index = 0;
var done = false;
var loop = {
next: function() {
if (done) {
return;
}
if (index < iterations) {
index++;
func(loop);
} else {
done = true;
callback();
}
},
iteration: function() {
return index - 1;
},
break: function() {
done = true;
callback();
}
};
loop.next();
return loop;
}
var asyncProc = ["Process1", "Process2", "Process3"]
asyncLoop
(
asyncProc.length,
(loop) => { promise1(asyncProc[loop.iteration()]).then((msg) =>{ console.log(msg); loop.next() }) },
() => { console.log("ALL DONE!")});
You cannot return the value of the "metric" synchronously if it is being modified asynchronously. You'll need to pass a callback into your method so the "metric" can be returned when it is ready.
Evaluator.prototype.asyncEval = function (predictor, callback) {
let self = this;
let metric = 0; //METRICS SHOULD BE UPDATED BY ASYNC FUNCTION
let callbacks = 0; // Keep a counter for the asynchronous callbacks
for (let i = 1; i < self.fullTraces.length; i++) {
let deltaTime = self.fullTraces[i][2] - this.fullTraces[i - 1][2];
let subTraces = self.fullTraces.slice(0, i);
// Queue up an asynchronous callback
predictor.predict(subTraces, (dist) => { // ASYNC FUNCTION
if (dist !== null) {
let result = dist.getTopK(1);
let pX = result[0][0][0];
let pY = result[0][0][1];
let x = self.fullTraces[i][0];
let y = self.fullTraces[i][1];
let a = pX - x;
let b = pY - y;
metric += Math.sqrt(a * a + b * b);
}
// Decrement the counter and check if we're done
if (--callbacks === 0) {
callback(metric / (self.fullTraces.length - 1));
}
});
// Increment the counter
callbacks++;
}
};

When babel processes async / await code does it bundle unrelated calls?

Is babels async / await code smart enough to see the code below:
async function alpha () {
let resultOne = await processNumber(5)
let resultTwo = await processNumber(5 + 8)
let resultThree = await processNumber(resultOne.number)
let resultFour = await processNumber(resultOne.number + resultThree.number)
return resultFour
}
As something like the following, where the first two promises within this function can happen together because the values needed to perform those operations does not need to wait for anything.
import Promise from 'bluebird'
async function beta () {
let {resultOne, resultTwo} = await Promise.props({
resultOne: processNumber(5),
resultTwo: processNumber(5 + 8)
})
let resultThree = await processNumber(resultOne.number)
let resultFour = await processNumber(resultOne.number + resultThree.number)
return resultFour
}
I would understand the alpha function as waiting for each async function call before it moves on to the next, where in beta resultOne and resultTwo are happening simultaneously, this is only possible because they don't need to wait on any other calls. I was wondering if this was truly the case or if babel does something behind the scenes to bundle these together.
I setup a benchmark between the two and it seems it does not account for this on it's own.
Here's the test:
import Promise from 'bluebird'
async function processNumber (int) {
await Promise.delay(500)
return {number: int + 3}
}
async function alpha () {
let resultOne = await processNumber(5)
let resultTwo = await processNumber(5 + 8)
let resultThree = await processNumber(resultOne.number)
let resultFour = await processNumber(resultOne.number + resultThree.number)
return resultFour
}
async function beta () {
let {resultOne, resultTwo} = await Promise.props({
resultOne: processNumber(5),
resultTwo: processNumber(5 + 8)
})
let resultThree = await processNumber(resultOne.number)
let resultFour = await processNumber(resultOne.number + resultThree.number)
return resultFour
}
async function main () {
const TEST_ALPHA = 'test alpha'
const TEST_BETA = 'test beta'
console.time(TEST_ALPHA)
let resultAlpha = await alpha()
console.log(resultAlpha)
console.timeEnd(TEST_ALPHA)
console.time(TEST_BETA)
let resultBeta = await beta()
console.log(resultBeta)
console.timeEnd(TEST_BETA)
return true
}
main()
.then(console.log)
.catch(console.error)
Here's the results:
thomasreggi#zx:PAS-api$ babel-node test.js
{ number: 22 }
test alpha: 2025ms
{ number: 22 }
test beta: 1508ms
true
thomasreggi#zx:PAS-api$ babel-node test.js
{ number: 22 }
test alpha: 2033ms
{ number: 22 }
test beta: 1511ms
true
In JS it is nearly impossible to make any strict statements whether a given expression is "unrelated" to another arbitrary expression (especially statically).
It happens because due to its highly-dynamic nature almost every expression may cause hidden (or not so hidden) side effects that may break the expected flow of the program.
For your very code it is really easy to break the code in case if both "unrelated" calls are triggered "simultaneously":
let isFirst = true;
async function processNumber(v) {
await Promise.delay(2000 - v * 100);
if (v < 10) {
if (!isFirst) {
throw new Error();
}
}
isFirst = false;
return { number: v + 3 };
}
This would work for alpha, but throw for beta.
If you know it would be fine and want to run them "in parallel" just use awaits correspondingly:
async function alpha () {
let one = processNumber(5)
let two = processNumber(5 + 8)
const resultOne = await one;
let resultThree = await processNumber(resultOne.number)
let resultFour = await processNumber(resultOne.number + resultThree.number)
return resultFour
}
Also note, that the resultTwo is not used anywhere in your code.

Categories

Resources