How to Know When All Promises Are Resolved in a Dynamic "Iterable" Parameter

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 returns:

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

if (Object.keys(obj[keys[j]]).length) {
promiseFunction(obj[keys[j]]);
} else {
// and some more async hell that all resolves correctly

If 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 found

const 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

I should not use loops within Promises

No, rather the other way round: You should not use promises within loops.

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

and

Promise.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

But when assigning the Promise to the variable, it just executes straight away.

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

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

I'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.

It's not as complicated as you seem to think. Create an array, add the promises to it and pass the array to 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



Leave a reply



Submit