Resolve the combined promise when some of them gets resolved - javascript

I have three promises. I need to combine them together to create one more promise.
Something like this Promise.all('url1','url2','url3') or if there is other possible way to achieve it.
Intent is to fire all of them in parallel.
I have requirement that when either two of them are resolved, then the final one will be resolved.
How I can achieve it?

The following might give you an idea. You have the chance to run the first resolving promise and the last one. But the logic is straightforward and you may play as you wish. I haven't included the error handling rejection logic but that should be fairly easy.
The bring() function returns a promise resolving in 100~200ms.
var bring = n => new Promise(v => setTimeout(v,~~(Math.random()*100)+100,`from url_${n}`)),
pcnt = 0,
proms = Array.from({length: 10})
.map((_,i) => bring(i).then(v => ++pcnt === 1 ? (console.log(`Do stg with the 1st resolving promise ${v} immediately`), [pcnt,v])
: pcnt < proms.length ? console.log(`Do nothing with intermediate resolving promise ${v}`)
: (console.log(`Do stg with the last resolving promise ${v} finally\n`), [pcnt,v])
)
);
Promise.all(proms)
.then(vs => console.log("Or when all completes, deal with the values returned from the first and last promise\n",vs.filter(d => !!d)));
.as-console-wrapper {
max-height: 100% !important
}

So you want to come up with a promise that resolves when, for example, 2 of 3 promises resolve. Promise.all wouldn't be the right thing to use because, well, you aren't waiting for all the promises to resolve. You'd have to do it manually:
const resolveAfterSeconds = function(sec) {
return new Promise(res => {
setTimeout(res, sec * 1000)
})
}
new Promise((resolve) => {
const promises = [
resolveAfterSeconds(1),
resolveAfterSeconds(2),
resolveAfterSeconds(3),
];
let resolveCount = 0;
promises.forEach(prom => {
prom.then(() => {
console.log('resolving');
resolveCount++;
if (resolveCount === promises.length - 1) resolve();
});
});
}).then(() => console.log('done'));

Related

Using multiple await()

Suppose I have two promises. Normally, the code cannot execute until all these promise are finished. In my code, suppose I have promise1 and promise 2
then I have await promise1 and await promise2. My code won't execute until both these promise are finished. However what I need is that if one of these two is finished then the code can execute and then we can ignore the other one.
That is possible because the code only needs one of these awaits to succeed. Is this possible to implement in javascript (nodejs) ?
Promise.any is what you're looking for:
Promise.any() takes an iterable of Promise objects. It returns a single promise that resolves as soon as any of the promises in the iterable fulfills, with the value of the fulfilled promise. If no promises in the iterable fulfill (if all of the given promises are rejected), then the returned promise is rejected with an AggregateError, a new subclass of Error that groups together individual errors.
Example:
const randomDelay = (num) => new Promise((resolve) => setTimeout(() => {
console.log(`${num} is done now`);
resolve(`Random Delay ${num} has finished`);
}, Math.random() * 1000 * 10));
(async() => {
console.log("Starting...");
const result = await Promise.any(new Array(5).fill(0).map((_, index) => randomDelay(index)));
console.log(result);
console.log("Continuing...");
})();
You can use Promise.any() function. You can pass an array of Promises as an input, and it will resolve as soon as first Promise resolves.
(async() => {
let promises = [];
promises.push(new Promise((resolve) => setTimeout(resolve, 100, 'quick')))
promises.push(new Promise((resolve) => setTimeout(resolve, 500, 'slow')))
const result = await Promise.any(promises);
console.log(result)
})()
Promise.race() or Promise.any()
If one of the promises's status changed whatever rejected or fulfilled, you can use Promise.race()
If you want one of the promises to be fulfilled, you should use
Promise.any()
Promise.any can continue to execute other code if any One Promise will resolved.
let i = 1;
const newPromise = () => {
return new Promise((resolve) => {
setTimeout(() => {
console.log(`promise${i++} resolved`);
resolve(true);
}, Math.random() * 1000)
});
}
(async() => {
await Promise.any([newPromise(), newPromise()]);
console.log("Code will executes if any promise resolved");
})()

How to wait for 2 of Promises in Promise.all to finish and then do another task

I'm currently developing a program where I need to use Promise.all to run a few functions at the same time. But before I can continue with the task, I need ONLY 2 of the promises to finish and then run the .then(), how would I go about doing this?
Example:
await Promise.all([Task1(),Task2(),Task3(),Task4(),Task5()]);
I need it to continue the code when only (for example) Task1 and Task 4 have finished.
I have tried to experiment by using a while loop waiting for Task1 and Task2 finish by settings variables on the finish, but that. doesn't work at all.
In a comment you seem to have said you specifically know that in advance which two are more urgent than the rest (and that in this example, it's Task1 and Task4).
Then just use Promise.all twice:
const allResults = Promise.all([
Promise.all([Task1(), Task4()])
.then(([result1, result4]) => {
// Those two are done, do what you like with `result1` and `result4`...
return [result1, result4];
}),
Task2(),
Task3(),
Task5()
])
.then(([[result1, result4], result2, result3, result5]) => {
// All five are done now, let's put them in order
return [result1, result2, result3, result4, result5];
})
.then(/*...*/)
.catch(/*...*/);
In there, I've preserved the overall 1, 2, 3, 4, 5 order in the outer chain by remapping the order in the overall then handler.
Originally, I assumed you wanted to wait until any two have finished, rather than a specific two. There's no built-in for that, but it's easy enough to write:
function enough(promises, min) {
if (typeof min !== "number") {
return Promise.all(promises);
}
let counter = 0;
const results = [];
return new Promise((resolve, reject) => {
let index = 0;
for (const promise of promises) {
let position = index++;
promise.then(
result => {
results[position] = result;
if (++counter >= min) {
resolve(results);
}
},
reject
);
}
});
}
Live Example:
function enough(promises, min) {
if (typeof min !== "number") {
return Promise.all(promises);
}
let counter = 0;
const results = [];
return new Promise((resolve, reject) => {
let index = 0;
for (const promise of promises) {
let position = index++;
promise.then(
result => {
results[position] = result;
if (++counter >= min) {
resolve(results);
}
},
reject
);
}
});
}
const delay = (ms, ...args) => new Promise(resolve => setTimeout(resolve, ms, ...args));
const rnd = () => Math.random() * 1000;
enough(
[
delay(rnd(), "a"),
delay(rnd(), "b"),
delay(rnd(), "c"),
delay(rnd(), "d"),
delay(rnd(), "e")
],
2
)
.then(results => {
console.log(results);
})
.catch(error => {
console.error(error);
});
One way to do it is by constructing a new array of the randomized promises, and then wait for those only:
let array = [Task1(),Task2(),Task3(),Task4(),Task5()];
// Select any two promises after running the randomization logic
let promises = Promise.all[array[1], array[3]];
promises
.then(() => {
// Do stuff here
});
I've seen tricks like this :
Promise.all(promises.map(p => p.catch(() => undefined)));
Quite unsafe though.
Original answer : here
OK, as I understand you, you want to do something like
const importantOnes = [Task1(), Task2()];
const remainingOnes = [Task3(), Task4(), Task5()];
const priorityPromise = Promise.all(importantOnes);
priorityPromise.then(doPriorityStuff);
Promise.all([priorityPromise, ...remainingOnes]).then(processTheCompleteData);
Promise.all() does not run your tasks simultaneously, it only awaits all Promises to resolve before resolving the returned Promise.
Your tasks will be run as soon as you create each Promise.
If you want to wait after specific tasks, only include those tasks in Promise.all:
const tasks = [Task2(), Task3(), Task5()];
const result1 = await Promise.all([Task1(), Task4()]);
// Task1 and Task4 are done here
const result2 = await Promise.all(tasks);
// All remaining tasks are done here

Promise resolved too fast

I am using Promises and in the meanwhile i have a loading animation.
the problme is that my promise is resolved fast and the loader is quickly disappear.
So i want to launch a promise and if the promise is resolved before 3 sec wait the remaining time.
Example
export const operation = () => {
const a = new Date();
const myPromise = doAction().then(() => {
const b = new Date();
if((b - a) < 3000)
operationIsDone();
else
setTimeout(() => {operationIsDone();}, b - a)
});
}
Is there any npm or a better way doing it?
Thanks in advance.
It is much easier to use a second promise that just runs the minimum waiting time. Then use Promise.all to wait for both to finish.
That way, your script will always wait at least the default delay but also longer if yourOwnPromise takes longer than that.
const wait = delay => new Promise(resolve => setTimeout(resolve, delay));
const doAction = () => wait(500); // TODO replace this with your own function
const yourOwnPromise = doAction();
yourOwnPromise.then(() => {
console.log('yourOwnPromise resolved now');
});
Promise.all([yourOwnPromise, wait(3000)]).then(() => {
console.log('both resolved now');
});
See Promise.all for details.

How to wait for few HTTP promises to complete and show a modal only if all the promises fail

I have two HTTP calls on a page and they are separate altogether.
vm.$onInit = function() {
....
....
//Get all the items only once during initialization
ds.getAllItems().then(function(result){
vm.items = result;
},function(error){
vm.errorInApi = true;
});
.....
.....
}
vm.getTimes = function(){
.....
.....
HttpWrapper.send(url,{"operation":'GET'}).then(function(times){
.....
}
If both the APIs fail then only I need to show a modal.
I can initiate a variable to true and on failure of the APIs, I can make that false and then only show the modal.
But then how long to wait for completion of all the APIs?
Hmm... simply invert the polarity of the promises and use Promise.all().
Promise.all() would normally resolve once all promises resolve, so once the promises are inverted, it resolves once all promises get rejected...
var invert = p => new Promise((v, x) => p.then(x, v));
Promise.all([Promise.reject("Error 404"), Promise.reject("Error WTF")].map(invert))
.then(v => console.log(v));
So as per #guest271314 comment i extend the solution in a silver spoon to show how inverting promises can be applied for this task.
var invert = p => new Promise((v, x) => p.then(x, v)),
prps = [Promise.reject("Error 404"), Promise.reject("Error WTF")]; // previously rejected promises
myButton.addEventListener('click', function(e){
setTimeout(function(...p){
p.push(Promise.reject("Error in Click Event Listener"));
Promise.all(p.map(invert))
.then(r => results.textContent = r.reduce((r,nr) => r + " - " + nr));
}, 200, ...prps);
});
<button id="myButton">Check</button>
<p id="results"></p>
If any of the promises including the previously obtained ones or the once in the event handler gets resolved you will get no output.
You can use async/await and .catch() to determine the number of rejected Promises, perform action if the number is equal to N, where N is the number of rejected Promise values required to perform action.
A prerequisite, as mentioned by #trincot, is to return the Promise from the function and return a value from the function passed to .then(), and throw an Error() from .catch() or function at second parameter of .then() see Why is value undefined at .then() chained to Promise?
const N = 2;
function onInit() {
return Promise.resolve("resolved")
}
function getTimes() {
return Promise.reject("rejected");
}
const first = onInit();
document.querySelector("button")
.onclick = async function() {
let n = 0;
const firstP = await first.catch(() => ++n);
const secondP = await getTimes().catch(() => ++n);
if (n === N) {
// do stuff if `n` is equal to `N`
} else {
// do other stuff
console.log(n, N)
}
};
<button>click</button>
You could use Promise.all.
First you should get the two promises in an array. To achieve that, it is probably useful if your two functions return the promise they create:
vm.$onInit = function() {
....
....
return ds.getAllItems().then(function(result){
vm.items = result;
},function(error){
vm.errorInApi = true;
});
}
vm.getTimes = function(){
.....
.....
return HttpWrapper.send(url,{"operation":'GET'}).then(function(times){
.....
});
}
The array would then be built from the return values:
var arr = [];
arr.push(vm.$onInit());
...
arr.push(vm.getTimes());
You write in a comment that getTimes "is called on some button click", so you would do that second push there.
Or, maybe you see another way to get these promises in an array... it does not matter much how you do it, as long as you achieve this.
Then (in that click handler) you need to detect the situation where both the promises are rejected. Promise.all can do that, but you need to flip the promise results:
// Flip promises so that a rejected one is regarded as fulfilled and vice versa:
arr = arr.map(p => p.then(res => { throw res }).catch(err => err));
// Detect that all original promises rejected, i.e. that the new promises all fulfill.
Promise.all(arr).then(function() {
// execute whatever you need to execute when both ajax calls failed
}).catch(err => err); // ignore

Promise.all().then() resolve?

Using Node 4.x. When you have a Promise.all(promises).then() what is the proper way to resolve the data and pass it to the next .then()?
I want to do something like this:
Promise.all(promises).then(function(data){
// Do something with the data here
}).then(function(data){
// Do more stuff here
});
But I'm not sure how to get the data to the 2nd .then(). I can't use resolve(...) in the first .then(). I figured out I can do this:
return Promise.all(promises).then(function(data){
// Do something with the data here
return data;
}).then(function(data){
// Do more stuff here
});
But that doesn't seem like the proper way to do it... What is the right approach to this?
But that doesn't seem like the proper way to do it..
That is indeed the proper way to do it (or at least a proper way to do it). This is a key aspect of promises, they're a pipeline, and the data can be massaged by the various handlers in the pipeline.
Example:
const promises = [
new Promise(resolve => setTimeout(resolve, 0, 1)),
new Promise(resolve => setTimeout(resolve, 0, 2))
];
Promise.all(promises)
.then(data => {
console.log("First handler", data);
return data.map(entry => entry * 10);
})
.then(data => {
console.log("Second handler", data);
});
(catch handler omitted for brevity. In production code, always either propagate the promise, or handle rejection.)
The output we see from that is:
First handler [1,2]
Second handler [10,20]
...because the first handler gets the resolution of the two promises (1 and 2) as an array, and then creates a new array with each of those multiplied by 10 and returns it. The second handler gets what the first handler returned.
If the additional work you're doing is synchronous, you can also put it in the first handler:
Example:
const promises = [
new Promise(resolve => setTimeout(resolve, 0, 1)),
new Promise(resolve => setTimeout(resolve, 0, 2))
];
Promise.all(promises)
.then(data => {
console.log("Initial data", data);
data = data.map(entry => entry * 10);
console.log("Updated data", data);
return data;
});
...but if it's asynchronous you won't want to do that as it ends up getting nested, and the nesting can quickly get out of hand.
Today NodeJS supports new async/await syntax. This is an easy syntax and makes the life much easier
async function process(promises) { // must be an async function
let x = await Promise.all(promises); // now x will be an array
x = x.map( tmp => tmp * 10); // proccessing the data.
}
const promises = [
new Promise(resolve => setTimeout(resolve, 0, 1)),
new Promise(resolve => setTimeout(resolve, 0, 2))
];
process(promises)
Learn more:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function
Your return data approach is correct, that's an example of promise chaining. If you return a promise from your .then() callback, JavaScript will resolve that promise and pass the data to the next then() callback.
Just be careful and make sure you handle errors with .catch(). Promise.all() rejects as soon as one of the promises in the array rejects.

Categories

Resources