Order of async functions execution - javascript

I am dealing with an old codebase and faced this situation where it's difficult for me to understand the order of execution after promises are resolved. I am more familiar with async/await syntax or with a chain of then-s, but not with this one. Here is the snippet:
_loadCaseDetail: funciton (arg1, arg2, arg3) {
var oDataModel = this.getOwnerComponent().getModel('db2');
loadLatestDatasetVersion(oDataModel).then(function (datasetVersion) {
// do something
});
loadCountries(oDataModel).then(function (countries) {
// do something
});
numberOfRulesetChanges(oDataModel).then(function (elements) {
// do something
});
return fireImplicitLock(caseUuid).then(function (lockData) {
// do something
}).then(function () {
// do something
})
}
loadLatestDatasetVersion, loadCountries, numberOfRulesetChanges, fireImplicitLock - all return promises
My question is: What would be the order in this case for all then-s that come after these promises?
Is it exactly sequential as it is or it's not and we can refactor it with say Promise.all?
Does it even need any refactoring?

What would be the order in this case for all then-s that come after these promises?
The then function will fire when the associated promise resolves. That's the point of asynchronous code. It goes away until it is ready to do the next thing.
we can refactor it with say Promise.all
You could use Promise.all to wait until multiple promises are resolved before doing anything with the resulting values, but that would be inefficient unless the then function for C requires data from A or B.

loadLatestDatasetVersion, loadCountries, numberOfRulesetChanges, fireImplicitLock - all return promises they will get inserted into Event loop one after another.
then part of promises will get executed once promises are resolved. Which promise's then will get executed depends upon the execution of the respective promise.
_loadCaseDetail: funciton (arg1, arg2, arg3) {
var oDataModel = this.getOwnerComponent().getModel('db2');
loadLatestDatasetVersion(oDataModel).then(function (datasetVersion) {
// do something
});
loadCountries(oDataModel).then(function (countries) {
// do something
});
numberOfRulesetChanges(oDataModel).then(function (elements) {
// do something
});
return fireImplicitLock(caseUuid).then(function (lockData) {
// do something
}).then(function () {
// do something
})
}
Promise.all is like a gate where you can pass array of promises and it will resolve only after all the promises are resolved.
let p1 = Promise.resolve(loadLatestDatasetVersion(oDataModel));
let p2 = Promise.resolve(loadCountries(oDataModel));
let p3 = Promise.resolve(numberOfRulesetChanges(oDataModel));
let p4 = Promise.resolve( fireImplicitLock(caseUuid)
let finalPromise = Promise.all([p1,p2,p3,p4]);
finalPromise.then(([datasetVersion,countries,elements,lockData])=>{
// do something here with datasetVersion, countries, elements, lockData you have all the params cause all the promises are resolved.
})
Same thing you can achieve using Promise.all like shown above.
More about promises
return fireImplicitLock(caseUuid).then(function (lockData) {
// do something
}).then(function () {
// do something
})
Above line will not return any result from promises, it will return Promise object with its status resolved/pending/rejected and value.

Related

useEffect() and async functions [duplicate]

I have a problem with waiting for my forEach loop, which has a promise inside, to finish. I can't find any real solution, that would make the script wait till the end, before continuing with the execution. I cannot make the someFunction synchronous.
makeTree: function (arr) {
arr.forEach(function (resource) {
someModule.someFunction(resource).then(function () { //a Promise
//do something with the resource that has been modified with someFunction
});
});
// do something after the loop finishes
}
Instead of forEach() use map() to create an array of promises and then use Promise.all()
let promiseArr = arr.map(function (resource) {
// return the promise to array
return someModule.someFunction(resource).then(function (res) { //a Promise
//do something with the resource that has been modified with someFunction
return res;
})
});
Promise.all(promiseArr).then(function(resultsArray){
// do something after the loop finishes
}).catch(function(err){
// do something when any of the promises in array are rejected
})
Try this,
makeTree: function (arr) {
var promises = [];
arr.forEach(function(resource) {
promises.push(someModule.someFunction(resource));
});
Promise.all(promises).then(function(responses) {
// responses will come as array of them
// do something after everything finishes
}).catch(function(reason) {
// catch all the errors
console.log(reason);
});
}
You can refer this link for more on Promise.all with simple examples.

asynchronously map asynchronous function [duplicate]

I have a problem with waiting for my forEach loop, which has a promise inside, to finish. I can't find any real solution, that would make the script wait till the end, before continuing with the execution. I cannot make the someFunction synchronous.
makeTree: function (arr) {
arr.forEach(function (resource) {
someModule.someFunction(resource).then(function () { //a Promise
//do something with the resource that has been modified with someFunction
});
});
// do something after the loop finishes
}
Instead of forEach() use map() to create an array of promises and then use Promise.all()
let promiseArr = arr.map(function (resource) {
// return the promise to array
return someModule.someFunction(resource).then(function (res) { //a Promise
//do something with the resource that has been modified with someFunction
return res;
})
});
Promise.all(promiseArr).then(function(resultsArray){
// do something after the loop finishes
}).catch(function(err){
// do something when any of the promises in array are rejected
})
Try this,
makeTree: function (arr) {
var promises = [];
arr.forEach(function(resource) {
promises.push(someModule.someFunction(resource));
});
Promise.all(promises).then(function(responses) {
// responses will come as array of them
// do something after everything finishes
}).catch(function(reason) {
// catch all the errors
console.log(reason);
});
}
You can refer this link for more on Promise.all with simple examples.

When do Q promises get executed?

I'm having a (seemingly fundamental) problem understanding promises. First the code:
'use strict';
var Q = require("q");
var mockPromise = function (statement) {
var deferred = Q.defer();
console.log("I'm running before I'm queued ...");
setTimeout(function () {
deferred.resolve(statement);
}, 5000);
return deferred.promise;
};
var promises = [
mockPromise("1st statement"),
mockPromise("2nd statement"),
mockPromise("3rd statement")
];
Q.all(promises)
.then(function (results) {
console.log(results);
});
Each promise function gets invoked upon adding it to the promise array, as opposed to when Q.all is called as I thought.
What am I not getting here?
How do I queue an array of promises without immediately invoking said promises?
Promises are objects. They are not 'executed'. They are 'resolved' or 'rejected'. When you create the array, you are executing the mockPromise() function three times. This function is immediately executed in that point of the code.
The mockPromise() function creates a deferred and returns the associated promise. It also sets a timer to resolve the returned promise in the future.
Q.all() just waits for the 3 promises to be 'resolved'. (technically it returns a new promise that will be resolved when the 3 previous promises are resolved)
If you want to execute the three async functions one after the other, I would recommend using the excellent async.js library. It provides many async flow control primitives. In your case you may be interested in series or waterfall methods.
It seems the confusion is that you understand the promise API to be designed for lazy evaluation, which is not the case.
Promises are a way of handling long running requests, they were designed to start IMMEDIATELY to minimize waiting time, and to utilize chaining and joining to clarify how the results of these long running requests should be processed.
You might try to utilize the api Q-Lazy which allows you to delay invocation of promises until they have been subscribed to.
You'd normally defer asynchronous functionality, not just a value. For example:
'use strict';
var Q = require("q");
var mockPromise = function (statement) {
var deferred = Q.defer();
console.log("I'm running before I'm queued ...");
setTimeout(function () {
deferred.resolve(statement());
}, 5000);
return deferred.promise;
};
var promises = [
mockPromise(function() {
console.log("running1");
return "1st statement";
}),
mockPromise(function() {
console.log("running2");
return "2nd statement";
}),
mockPromise(function() {
console.log("running3");
return "3rd statement";
}),
];
Q.all(promises)
.then(function (results) {
console.log(results);
});
Note that the deferred functionality is going to run regardless of whether you ever call a .then on a promise.
Let me show a sample using standard promises. They work pretty much the same as Q promises:
function mockPromise(value) {
return new Promise(resolve => {
console.log("I'm not running before I'm queued ...");
setTimeout(() => {
resolve(value);
}, 1000);
});
}
mockPromise("1st promise").then(x => {
console.log(x);
return mockPromise("2nd promise");
}).then(x => {
console.log(x);
return mockPromise("3nd promise");
}).then(x => {
console.log(x);
});

How to call asynchronous method recursively with different parameters

I have a method of rest call using request module which is restRequest() which returns response as promise which is asynchronous method, I have to call this method recursively with different parameters after getting the each results and passing that result to same method.
Example code:
restRequest(url, "POST").then(function(response) {
restRequest(secondUrl, 'GET', response).then(function(response2) {
}):
});
will this works, or any other things are there to solve this one.
I would use the async library for this
Specifically the waterfall
Which would work like
async.waterfall([
function firstRequest(callback) {
restRequest(url, "POST").then(function(response) {
callback(null, response);
});
},
function secondRequest (data, callback) {
restRequest(secondUrl, 'GET', data).then(function(response2) {
callback();
});
}
], function (err, result) {
// Handle err or result
});
Sorry for formatting I'm on mobile.
You can read about how async.waterfall works from the link above.
Your method works but depending on how many requests you have you can end up with quite a deep callback hell
But since you are using promises you can just return your promise chain like
restRequest(url, "POST")
.then(function(resp) {
return restRequest(secondUrl, "GET", resp);
})
.then(function(resp) {
return restRequest(thirdUrl, "GET", resp);
});
.then(function(resp) {
// do whatever keep the chain going or whatever
})
.catch(function(error) {
// if any of the promises error it will immediately call here.
});
With promises you can return a new promise from within a .then and just keep the chain going infinitely.
I'm just biased for async as i think it really improves readability when used right.
you could do something like:
let requestParams = [
[url, 'POST'],
[secondUrl, 'GET'],
...
];
function callRecursive(response){
if(!requestParams.length) return Promise.resolve(response);
let params = requestParams.shift();
if(response) params.push(response);
return restRequest(...params).then(callRecursive);
}
callRecursive().then(successCallbk).catch(errCallBk);
You can supply one or more arguments to bind to your partially applied function.
restRequest(url,"POST").then(restRequest.bind(this,secondUrl, "GET"))
.then(restRequest.bind(this,thirdUrl, "GET"));
Since these are fired off in serial, what you really have is a simple chain of functions (some return promises, some might not) that can compose (or sequence, here) together, which I find to be a neat way to isolate out everything you want to happen and then combine behaviors as needed. It's still a Promise chain under the hood, but expressed as a series. First, a few utility methods to help:
var curry = (f, ...args) =>
(f.length <= args.length) ? f(...args) : (...more) => curry(f, ...args, ...more);
var pipeP = (...fnlist) =>
acc => fnlist.reduce( (acc,fn) => acc.then(fn), Promise.resolve(acc));
then
//make restRequest only return a Promise once it's given its 3rd argument
var restRequest = autocurry(restRequest);
//define what our requests look like
var request1 = restRequest('firstUrl', "POST");//-> curried function, not yet called
var request2 = restRequest('secondUrl', 'GET');//-> curried function, not yet called
//define some simple methods to process responses
var extractURL = x => x.url;//-> simple function
var extractData = x=> x.data;//-> simple function
//final behaviors, i.e. do something with data or handle errors
//var handleData = ... //-> do something with "data"
//var handleError = ... //-> handle errors
//now, create a sort of lazy program chain waiting for a starting value
//that value is passed to request1 as its 3rd arg, starting things off
var handleARequest = pipeP(request1, extractURL, request2, extractData);
//and execute it as needed by passing it a starting request
handleARequest({postdata:5}).then(handleData).catch(handleErrors);
Recursion is the most obvious approach but it's not necessary. An alternative is to build a .then() chain by reducing an array of known parameters (urls and methods).
The process is presented here under "The Collection Kerfuffle".
function asyncSequence(params) {
return params.reduce(function(promise, paramObj) {
return promise.then(function(response) {
return restRequest(paramObj.url, paramObj.method, response);
});
}, Promise.resolve(null)); // a promise resolved with the value to appear as `response` in the first iteration of the reduction.
}
This will cater for any number of requests, as determined by the length of the params array.
Call as follows :
var params = [
{url:'path/1', method:'POST'},
{url:'path/2', method:'GET'},
{url:'path/3', method:'POST'}
];
asyncSequence(params).then(function(lastResponse) {
//all successfully completed
}).catch(function(e) {
// something went wrong
});

Passing an extra parameter in the middle of a Promise chain

I need to throw in a userID param in the middle of a promise chain(it is the only promise that needs it). All the promises should execute in a synchronous order.
SideNote- All the similar examples on stackoverflow are all a bit different- like using lambda functions(I use declared functions).So I'm still not quite sure.
var initialize = function(userID) {
var firstPromise = Module2.getFirstPromise();
firstPromise.then(getSecondPromise)
.then(getThirdPromise)
.then(getFourthPromise) //<----Fourth promise needs userID
.then(getFifthPromise)
.then(Utils.initializeComplete);
}
All the promises are functions that look like this:
var thirdPromise = function() {
return new Promise(function(resolve, reject) {
//fetch data
//On Return, Store it
resolve() //Nothing needed to passed down from this promise
});
});
}
I'm trying this, and it "works", but I'm not sure if that is how I am "suppose" to handle something like this. :)
var initialize = function(userID) {
var firstPromise = Module2.getFirstPromise();
firstPromise.then(getSecondPromise)
.then(getThirdPromise)
.then(function(){ return fourthPromise(userID)})
.then(getFourthPromise)
.then(Utils.initializeComplete);
}
Note: getFirstPromise is coming from a different module in my code. That shouldn't be important to the question though :)
Assuming that firstPromise is really a promise but secondPromise and so on are actually functions returning promises, then yes, what you've done is how you're supposed to do that. Here's a live example on Babel's REPL, which looks like this:
function doSomething(userID) {
getFirstPromise()
.then(getSecondPromise)
.then(getThirdPromise)
.then(() => getFourthPromise(userID))
// Or .then(function() { return getFourthPromise(userID); })
.then(getFifthPromise)
.catch(err => {
console.log("Error: " + err.message);
});
}
doSomething("foo");
function getFirstPromise() {
console.log("getFirstPromise called");
return new Promise(resolve => {
setTimeout(() => {
resolve("one");
}, 0);
});
}
// and then second, third, fourth (with user ID), and fifth
(If you don't use arrow functions, just replace them with the function form.)
Note the catch in the example above. Unless you have a really good reason not to, a promise chain should always have a .catch if you don't return the result.
Your solution is perfectly fine. It might be easier to understand with concrete signatures.
If your thirdPromise doesn't take anything and doesn't return anything its signature might be written (pseudocode assuming a -> b is a function from a to b) as _ -> Promise (_). If it returns some value a, it would be _ -> Promise (a). If it took something and returned something it might be a -> Promise (b)
So you can reason about your promise chains as about functions taking some value and returning some other value wrapped in a promise. However, your fourthPromise looks differently:
fourthPromise : UserId -> a -> Promise (b)
Which can be written as:
fourthPromise : UserId -> (a -> Promise (b))
It takes one parameter before becoming an actual promise you can chain. In a way, it's a template of a promise.
If you want the plain .then chain in the main function, try to write getFourthPromise as a factory function
function getSomethingByUserID(userId) {
return function() {
return new Promise(function(resolve) {
//your actual async job
resolve('a result');
});
};
}
Then you will get plan list of thens
var firstPromise = Module2.getFirstPromise()
.then(getSecondPromise)
.then(getSomethingByUserID(userId))
.then(Utils.initializeComplete);
Do not forget that if you missed to provide userId to getSomethingByUserID, it will not work.

Categories

Resources