How to know when all Promises are Resolved in a dynamic iterable parameter?
There's no way out. You have to put all the promises in the array before calling Promise.all
in it. In the example you presented, that's as simple as moving the last line to the top.
In case you are asynchronously filling the array, you should get a promise for that array, and use .then(Promise.all.bind(Promise))
. If you don't know when you stop adding promises, this is impossible anyway as they might never all be resolved at all.
Regarding your "beauty example", you will want to learn about the magic of chaining. As I previosly said in the comments, you have to
return
a promise from every function in which you are doing anything asynchronous. Indeed, just add the missing return
s:function userClikOnLogIn() {
return $http.get('/login/user/password').then(function(data){
// ^^^^^^
if (data.logguedOk) {
return $http.get('/checkIfIsAdmin').then(function(data){
// ^^^^^^
if (data.yesHeIsAnAdmin) {
return $http.get('/getTheNameOfTheUser').then(function(data){
// ^^^^^^
if(data.userHasName) {
return $http.get('/getCurrentDate').then(function(data){
// ^^^^^^
currentDate = data.theNewCurrentDate;
});
}
});
}
});
}
});
}
userClikOnLogIn().then(function showAllTheInformation() {
// ^^^^^ now you can chain onto it!
alert('Hi ' + name + ' today is:' + date);
});
There is no array of promises here that dynamically grows, it's just that every function is returning a promise for the (asynchronous) result of the things it does. Dynamically fill a Promise.all()
Ifif (Object.keys(obj[keys[j]]).length) {
promiseFunction(obj[keys[j]]);
} else {
// and some more async hell that all resolves correctly
Object.keys(obj[keys[j]]).length
is true
then you never call resolve
or reject
so the promise never resolves.(Note that calling promiseFunction
recursively creates a new promise which is never put into Promise.all
).
Since some of your promises don't resolve, Promise.all
won't either.
You probably need something more along the lines of:
var promises = [];
processObject(obj);
Promise.all(promises).then(etc etc);
function processObject(object) {
for ( loop; over; object ) {
if (condition) {
processObject(object[something]);
} else {
promises.push(new Promise( (res, rej) => {
// so something async
});
}
}
}
Promise.all() with dynamically sized array of requests using await
Okay, I'm going to use fetch
API to demonstrate the usage of Promise.all()
Normal usage (for one fetch
call)
let user = { username: 'john.doe', password: 'secret' };
try{
let res = await fetch('https://example.com/user/', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(user)
})
console.log('User creation response: ', res);
}
catch(err){
console.error('User creation error: ', err);
}
Now let's use Promise.all()
const users = [
{ username: 'john.doe', password: 'secret' },
{ username: 'jane.doe', password: 'i-love-my-secret' }
];
const requests = [];
// push first request into array
requests.push(
fetch('https://example.com/user/', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(user[0])
})
);
// push second request into array
requests.push(
fetch('https://example.com/user/', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(user[1])
})
);
try{
const responses = await Promise.all(requests);
console.log('User creation responses: ', responses);
}
catch(err){
console.log('User creation error: ', err);
}
Promise.allSettled where promises list is varied incrementally
Solution
I know you may be confused on this question a lot. Here is the solution I foundconst promises = []
const timeout = ms => {
const promise = new Promise(resolve => setTimeout(resolve, ms))
promises.push(promise)
return promise
}
const someFunc = () =>
timeout(1000).then(() => {
console.log('b')
timeout(1000).then(() => console.log('c'))
})
async function main() {
console.log('a')
someFunc()
let i = 0;
while (promises.length > i) {
i = promises.length
await Promise.allSettled(promises)
}
console.log('d')
}
main()
JavaScript create promise queue without async/await
Here is updated PromiseQueue
implementation inspired from the other answer.
class PromiseQueue {
constructor() {
this._queue = [];
}
add(promise) {
this._queue.push(promise);
}
all() {
return this._all([]);
}
_all(results) {
let all = Promise.all(this._queue);
this._queue = [];
return all.then((newResults) => {
results.push(...newResults);
if (this._queue.length > 0) {
return this._all(results);
}
return results;
});
}
}
Proper way of using loops in Promises
No, rather the other way round: You should not use promises within loops.I should not use loops within Promises
Of course that's too generic as well. Sometimes you just need a loop structure. What you must not do is forget to collect the promises that are created in the loop body in some iterable that you can pass to Promise.all
, to await all the asynchronous things started in that loop.
The map
method as suggested in the article naturally does that, you just have to return
a promise from the callback (as always). Using for
/while
/.forEach
makes it a bit harder as you have to manually push the promises in some array (which is not only ugly but also errorprone).
However, if you are not dealing with asynchronous tasks inside your loop, you can do whatever you want. For example, both
Promise.all(values.filter(syncPredicate).map(asyncFn))
andPromise.all(promises).then((values) => values.filter(syncPredicate))
are totally fine. It does become a bit more complicated when you have an asynchronous filter predicate, I'd recommend to look out for a promise utility library in that case.Also you will have to realise that asynchronous tasks started from within a synchronous loop construct will run in parallel. If you intend to run them sequentially (await each iteration), you should try to formulate the loop using a recursive structure.
How can I modify the array passed to Promise.all
Thank you for the link to How to know when all Promises are Resolved in a dynamic "iterable" parameter?. Using info from this thread I was able to come up with a working solution as follows:
private readonly loadingPromises: Promise<any>[];
private async finishLoading(): Promise<void> {
var completedCount = 0;
while (this.loadingPromises.length != completedCount) {
var waitCount = this.loadingPromises.length;
await Promise.all(this.loadingPromises);
completedCount = waitCount;
}
this.loadingPromises.length = 0;
}
async somePublicMethod() {
return this.finishLoading().then(() => {
//... do stuff here
});
}
JS Promises: Promises are executing/resolving when assigning to a variable
Yes, that is what the Promise constructor does (it executes its callback function immediately and passes it resolve and reject functions that the callback can call when it is finished).But when assigning the Promise to the variable, it just executes straight away.
If you want something you can execute later, then use a function instead of a promise. (The function could return a promise).
Execute promise after all promises were resolved
It's not as complicated as you seem to think. Create an array, add the promises to it and pass the array toI've read about Promise.all method, and that I should pass a promises array, the question is that I don´t know how many promises would be returned, and in my code "renameTask" is the variable that acts as the promise receiver.
Promise.all
:var promises = []; // array of promises
files.forEach(function (file) {
var extension = path.extname(file);
if (extension === ".jpg") {
var renameTask = renameImages(file, newFileName, extension);
promises.push(renameTask.then(...)); // add promise
}
});
Promise.all(promises).then(...); // use array
Related Topics
How to Make a Directive Update Ng-Model on Jquery on Event
Angular Ui-Router $Urlrouterprovider .When Not Working When I Click <A Ui-Sref="...">
Gmail Extension, Sendmessage to Background from Page Context
How to Write Regex to Validate Dates
How to Detect If JavaScript Files Are Loaded
Differencebetween Settimeout(Fn, 0) and Settimeout(Fn, 1)
JavaScript Curry: What Are the Practical Applications
How to Get the Focused Element with Jquery
Ng-Model Does Not Update Controller Value
External Resource Not Being Loaded by Angularjs
Arrow VS Classic Method in Es6 Class
Javascript: Cancel/Stop Image Requests
How to Change the Pop-Up Position of the Jquery Datepicker Control