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.
Related
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.
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.
I'm rewriting some database code from synchronous (LocalStorage) to asynchronous (IndexedDB). I'm using the Alasql library and Promises. One of the problems I encounter is that when doing things asynchronously, sometimes it seems impossible to avoid duplicating code.
For example, my synchronous (pseudo) code could be something like this (idExists, doUpdate and doInsert are database methods):
function insertOrUpdate(data,id)
{
var result = null;
if (!idExists(id)) // idExists returns a boolean
result = doInsert(data,id); // doInsert returns an object
else
result = doUpdate(data,id); // doUpdate returns an object
doSomething(result);
}
With asynchronous code, it becomes something like this:
function insertOrUpdate(data,id)
{
var promise1 = idExists(id); // idExists returns a promise
promise1.then( function(id_exists) {
if (id_exists) {
var promise2 = doInsert(data,id); // doInsert returns a promise
promise2.then( function(result) {
doSomething(result);
});
}
else {
var promise3 = doUpdate(data,id); // doUpdate returns a promise
promise3.then( function(result) {
doSomething(result);
});
}
});
}
Here I have to call doSomething at two places in the code. Is there a way to avoid this situation? I'm new to promises and my apologies if this has been asked before, but I couldn't find an answer to this.
You can return a promise from a chained callback, which then gets inserted into the promise chain. Your code can, and should, be written as:
function insertOrUpdate(data, id) {
return idExists(id)
.then(function (exists) {
return exists ? doInsert(data, id) : doUpdate(data, id);
})
.then(doSomething);
}
The promises from doInsert or doUpdate will be chained into the existing chain from idExists, so the final .then(doSomething) will be executed with their result.
You can store promise into variable and call doSomething only once:
function insertOrUpdate(data, id) {
return idExists(id).then(function(id_exists) {
var promise = id_exists ? doInsert(data, id) : doUpdate(data, id)
return promise.then(doSomething)
});
}
I've had a problem trying to wrap my mind around JavaScript promises. Here is my lately frustration:
var rp = require('request-promise');
function process1000() {
//gets 1000 objects from DB to process
get1000objects()
.then(function (docs) {
Promise.map(docs, addApiData)
})
.then(function (objects) {
console.log(objects)
})
}
function addApiData(element) {
return rp(url).then(function (res) {
resolve(res);
})
.catch(console.error);
}
When rp(url) is called in addApiData(), it doesn't wait until the function is resolved before beginning the next item in the array that I'm passing to Promise.map in process1000(). As soon as rp(url) is called in addApiData, addApiData is called on the next item in the array. The second .then() function in process1000 is then called, prematurely before everything has had a chance to resolve in Promise.map. I've tried a few different libraries to make my GET request but they're all having this same issue. What am I doing wrong here? Any help would be greatly appreciated.
I think you're looking for something more like this:
var rp = require('request-promise');
function process1000() {
//gets 1000 objects from DB to process
get1000objects()
.then(function (docs) {
return Promise.map(docs, addApiData)
})
.then(function (objects) {
console.log(objects);
})
}
function addApiData(element) {
return rp(url)
.catch(console.error);
}
The main point here is that you need to make sure you're returning the promises, or the next then won't know what to do with its value.
Also, try to avoid the Promise constructor antipattern.
In other words, if you return a promise, you don't need to return then as well.
These two are equivalent:
return rp(url).then(function(res) { return res; });
// and
return rp(url);
They both return a promise (rp(url) returns a promise, and rp(url).then( /* ... */) returns a promise).
I have a list of objects. The objects are passed to a deferred function. I want to call the function with the next object only after the previous call is resolved. Is there any way I can do this?
angular.forEach(objects, function (object) {
// wait for this to resolve and after that move to next object
doSomething(object);
});
Before ES2017 and async/await (see below for an option in ES2017), you can't use .forEach() if you want to wait for a promise because promises are not blocking. Javascript and promises just don't work that way.
You can chain multiple promises and make the promise infrastructure sequence them.
You can iterate manually and advance the iteration only when the previous promise finishes.
You can use a library like async or Bluebird that will sequence them for you.
There are lots of different alternatives, but .forEach() will not do it for you.
Here's an example of sequencing using chaining of promises with angular promises (assuming objects is an array):
objects.reduce(function(p, val) {
return p.then(function() {
return doSomething(val);
});
}, $q.when(true)).then(function(finalResult) {
// done here
}, function(err) {
// error here
});
And, using standard ES6 promises, this would be:
objects.reduce(function(p, val) {
return p.then(function() {
return doSomething(val);
});
}, Promise.resolve()).then(function(finalResult) {
// done here
}, function(err) {
// error here
});
Here's an example of manually sequencing (assuming objects is an array), though this does not report back completion or errors like the above option does:
function run(objects) {
var cntr = 0;
function next() {
if (cntr < objects.length) {
doSomething(objects[cntr++]).then(next);
}
}
next();
}
ES2017
In ES2017, the async/wait feature does allow you to "wait" for a promise to fulfill before continuing the loop iteration when using non-function based loops such as for or while:
async function someFunc() {
for (object of objects) {
// wait for this to resolve and after that move to next object
let result = await doSomething(object);
}
}
The code has to be contained inside an async function and then you can use await to tell the interpreter to wait for the promise to resolve before continuing the loop. Note, while this appears to be "blocking" type behavior, it is not blocking the event loop. Other events in the event loop can still be processed during the await.
Yes you can use angular.forEach to achieve this.
Here is an example (assuming objects is an array):
// Define the initial promise
var sequence = $q.defer();
sequence.resolve();
sequence = sequence.promise;
angular.forEach(objects, function(val,key){
sequence = sequence.then(function() {
return doSomething(val);
});
});
Here is how this can be done using array.reduce, similar to #friend00's answer (assuming objects is an array):
objects.reduce(function(p, val) {
// The initial promise object
if(p.then === undefined) {
p.resolve();
p = p.promise;
}
return p.then(function() {
return doSomething(val);
});
}, $q.defer());
check $q on angular:
function outerFunction() {
var defer = $q.defer();
var promises = [];
function lastTask(){
writeSome('finish').then( function(){
defer.resolve();
});
}
angular.forEach( $scope.testArray, function(value){
promises.push(writeSome(value));
});
$q.all(promises).then(lastTask);
return defer;
}
The easiest way is to create a function and manually iterate over all the objects in the array after each promise is resolved.
var delayedFORLoop = function (array) {
var defer = $q.defer();
var loop = function (count) {
var item = array[count];
// Example of a promise to wait for
myService.DoCalculation(item).then(function (response) {
}).finally(function () {
// Resolve or continue with loop
if (count === array.length) {
defer.resolve();
} else {
loop(++count);
}
});
}
loop(0); // Start loop
return defer.promise;
}
// To use:
delayedFORLoop(array).then(function(response) {
// Do something
});
Example is also available on my GitHub:
https://github.com/pietervw/Deferred-Angular-FOR-Loop-Example
I use a simple solution for a connection to a printer that wait till the promise is over to go to the next.
angular.forEach(object, function(data){
yourFunction(data)
.then(function (){
return;
})
})
It might help someone as I tried several of above solution before coming up with my own that actually worked for me (the other ones didn't)
var sequence;
objects.forEach(function(item) {
if(sequence === undefined){
sequence = doSomethingThatReturnsAPromise(item)
}else{
sequence = sequence.then(function(){
return doSomethingThatReturnsAPromise(item)
});
}
});
It worked for me like this. I don't know if it is a right approach but could help to reduce lines
function myFun(){
var deffer = $q.defer();
angular.forEach(array,function(a,i) {
Service.method(a.id).then(function(res) {
console.log(res);
if(i == array.length-1) {
deffer.resolve(res);
}
});
});
return deffer.promise;
}
myFun().then(function(res){
//res here
});