How to chain a Promise.all with other Promises? - javascript

I want to execute my code in the following order:
Promise 1
Wait for 1 to be done, then do Promise 2+3 at the same time
Final function waits for Promise 2+3 to be done
I'm having some trouble figuring it out, my code so far is below.
function getPromise1() {
return new Promise((resolve, reject) => {
// do something async
resolve('myResult');
});
}
function getPromise2() {
return new Promise((resolve, reject) => {
// do something async
resolve('myResult');
});
}
function getPromise3() {
return new Promise((resolve, reject) => {
// do something async
resolve('myResult');
});
}
getPromise1()
.then(
Promise.all([getPromise2(), getPromise3()])
.then() // ???
)
.then(() => console.log('Finished!'));

Just return Promise.all(...
getPromise1().then(() => {
return Promise.all([getPromise2(), getPromise3()]);
}).then((args) => console.log(args)); // result from 2 and 3

I know it's an old thread, but isn't
() => {return Promise.all([getPromise2(), getPromise3()]);}
a little superfluous? The idea of fat arrow is that you can write it as:
() => Promise.all([getPromise2(), getPromise3()])
which makes the resulting code somewhat clearer:
getPromise1().then(() => Promise.all([getPromise2(), getPromise3()]))
.then((args) => console.log(args)); // result from 2 and 3
Anyway, thanks for the answer, I was stuck with this :)

Related

A .then doesn't run after the promise gets resolved on AWS Lambda

Code:
var promiseResolve, promiseReject;
function iterateRules(rules, params, i = 0) {
CWevents.putRule(rules[i], function(err, data) { // This is an async function with a callback
if (err) console.log(err); promiseReject(err)
responses++
params['Lambda_invoke']['SourceArns'].push(data.RuleArn)
if(responses == rules.length){ promiseResolve("Success"); console.log("Resolved")}
// After two responses are confirmed, this if does run and I get the "Resolved"
})
i++
if(i < rules.length){
setTimeout( function(){
iterateRules(params['CW_rules']['Rules'], params, i)
}, 50)
}
}
new Promise((resolve, reject) => {
resolve()
// This part is added solely for the code to make sense, it's taken out of a
// bigger script and lots of unnecessary data is removed
})
.then((Item) => {
return new Promise((resolve, reject) => {
promiseReject = reject;
promiseResolve = resolve;
iterateRules(params['CW_rules']['Rules'], params)
})
}) .then((res) => {
console.log("This ran")
})
The iterateRules function is supposed to run an asynchronous function multiple times, and resolve the Promise it's called in when the last response is acquired. The if(responses == rules.length) does run as expected and logs the "Resolved" in the console. Everything is successful, no errors.
Beneath is the context of this code, the iterateRules function is executed, but the .then that comes after, isn't. If I put a resolve() inside the promise directly, it does execute. What might be wrong with this script? I tried running a simple version of it that looks like this separately:
var res, rej;
function dude(){
res()
}
new Promise((resolve, reject) => {
res = resolve;
dude()
}).then((dude) => {
console.log("resolved")
})
And it does in fact work, so I'm very confused. What might cause the problem?
I would make iterateRules() return a promise (since it is asynchronous). I would also promisify the CWevents.putRule() function so you can accomplish some code like the below:
function iterateRules(rules, params) {
return Promise.all(rules.map(rule => {
return CWevents.putRule(rule)
})).then((data) => {
params['Lambda_invoke']['SourceArns'].push(data.RuleArn)
})
}
Then your handler for iterateRules would become:
iterateRules(rules,params).then(()=>{
// Do something...
})

returning a promise from the then of another promise

i am a little new at this. Please help
I am trying to return a promise from a function. This seems to work well until I try to return the promise in the then of an existing promise.
I am trying to do this in a FireFox extension. The code below is written for node.js as I thought it might give more explanation. I cannot figure out how to make it work.
function topLevel calls function level2. level2 waits for a promise to resolve and then returns a promise to level1. level1 logs in its .then.
level2 calls level3 after its internal promise is resolved and returns a new promise
In the example all promises resolve immediately. The example is paired down to the essentials
function topLevel() {
level2()
.then(() => {
console.log("topLevel resolved")
})
}
let testError=true;
function level2() {
if(testError) {
new Promise((resolve, reject) => {
resolve("Level 2");
})
.then(() => {
return (level3());
})
}
else {
return (level3());
}
}
function level3(){
return (new Promise((resolve, reject) => {
resolve("Level 3");
}));
}
topLevel();
there is a variable "testError" which changes the behavior of level 2. When set to "true" level2 waits for a promise and returns a promise from level3 in that promises then statement. level3 is run but the console.log in level1 never executes and an error results. When set to false everything works (promise from level3 is returned directly rather than in .then. The error is Cannot read property 'then' of undefined in toplevel .then.
The firefox webextension code from a background script is below
browser.browserAction.onClicked.addListener(topLevel);
function topLevel() {
level2()
.then(() => {
console.log("topLevel resolved")
})
}
function level2() {
new Promise((resolve, reject) => {
resolve("Level 2");
})
.then(() => {
return (level3());
})
}
function level3(){
return (new Promise((resolve, reject) => {
resolve("Level 3");
}));
}
It causes a warning in level2 between the .then and return statements that
"level2 is undefined". and again the log in level one never happens.
Any way to make this work?? I need to rely on this pattern
level2 does not return anything when testError is true.
The thenable (the function given to then) is a function. The return statement inside the thenable only concern the thenable itself (just like any function).
Change to
function level2() {
if (testError) {
return new Promise((resolve, reject) => {
resolve("Level 2");
}).then(() => {
return level3();
});
}
return level3();
}
I think it's as simple as adding a return before new Promise... in line 12. Revised code:
function topLevel() {
level2()
.then(() => {
console.log("topLevel resolved")
})
}
let testError = true;
function level2() {
if(testError) {
// Added "return" here
return new Promise((resolve, reject) => {
resolve("Level 2");
})
.then(() => {
return (level3());
})
}
else {
return (level3());
}
}
function level3() {
return (new Promise((resolve, reject) => {
resolve("Level 3");
}));
}
You need to return the new Promise you create in the level2 function.

What are some typical use-cases for `Promise.race()`? [duplicate]

As far as I know, there are two options about promise:
promise.all()
promise.race()
Ok, I know what promise.all() does. It runs promises in parallel, and .then gives you the values if both resolved successfully. Here is an example:
Promise.all([
$.ajax({ url: 'test1.php' }),
$.ajax({ url: 'test2.php' })
])
.then(([res1, res2]) => {
// Both requests resolved
})
.catch(error => {
// Something went wrong
});
But I don't understand what does promise.race() is supposed to do exactly? In other word, what's the difference with not using it? Assume this:
$.ajax({
url: 'test1.php',
async: true,
success: function (data) {
// This request resolved
}
});
$.ajax({
url: 'test2.php',
async: true,
success: function (data) {
// This request resolved
}
});
See? I haven't used promise.race() and it behaves like promise.race(). Anyway, is there any simple and clean example to show me when exactly should I use promise.race() ?
As you see, the race() will return the promise instance which is firstly resolved or rejected:
var p1 = new Promise(function(resolve, reject) {
setTimeout(resolve, 500, 'one');
});
var p2 = new Promise(function(resolve, reject) {
setTimeout(resolve, 100, 'two');
});
Promise.race([p1, p2]).then(function(value) {
console.log(value); // "two"
// Both resolve, but p2 is faster
});
For a scenes to be used, maybe you want to limit the cost time of a request :
var p = Promise.race([
fetch('/resource-that-may-take-a-while'),
new Promise(function (resolve, reject) {
setTimeout(() => reject(new Error('request timeout')), 5000)
})
])
p.then(response => console.log(response))
p.catch(error => console.log(error))
With the race() you just need to get the returned promise, you needn't care about which one of the promises in the race([]) firstly returned,
However, without the race, just like your example, you need to care about which one will firstly returned, and called the callback in the both success callback.
I've used it for request batching. We had to batch tens of thousands of records into batches for a long running execution. We could do it in parallel, but didn't want the number of pending requests to get out of hand.
Race lets us keep a fixed number of parallel promises running and add one to replace whenever one completes
const _ = require('lodash')
async function batchRequests(options) {
let query = { offset: 0, limit: options.limit };
do {
batch = await model.findAll(query);
query.offset += options.limit;
if (batch.length) {
const promise = doLongRequestForBatch(batch).then(() => {
// Once complete, pop this promise from our array
// so that we know we can add another batch in its place
_.remove(promises, p => p === promise);
});
promises.push(promise);
// Once we hit our concurrency limit, wait for at least one promise to
// resolve before continuing to batch off requests
if (promises.length >= options.concurrentBatches) {
await Promise.race(promises);
}
}
} while (batch.length);
// Wait for remaining batches to finish
return Promise.all(promises);
}
batchRequests({ limit: 100, concurrentBatches: 5 });
It's a piece to build a timeout system, where:
the request/computation may be canceled by another channel
it will still be used later, but we need an interaction now.
For an example of the second, one might show a spinner "instantly" while still defaulting to show real content if it comes in fast enough. Try running the below a few times - note at least some console message comes "instantly". This might normally be attached to perform operations on a UI.
The key to note is - the result of Promise.race is much less important than the side effects (though, this then is a code smell).
// 300 ms _feels_ "instant", and flickers are bad
function getUserInfo(user) {
return new Promise((resolve, reject) => {
// had it at 1500 to be more true-to-life, but 900 is better for testing
setTimeout(() => resolve("user data!"), Math.floor(900*Math.random()));
});
}
function showUserInfo(user) {
return getUserInfo().then(info => {
console.log("user info:", info);
return true;
});
}
function showSpinner() {
console.log("please wait...")
}
function timeout(delay, result) {
return new Promise(resolve => {
setTimeout(() => resolve(result), delay);
});
}
Promise.race([showUserInfo(), timeout(300)]).then(displayed => {
if (!displayed) showSpinner();
});
Inspiration credit to a comment by captainkovalsky.
An example of the first:
function timeout(delay) {
let cancel;
const wait = new Promise(resolve => {
const timer = setTimeout(() => resolve(false), delay);
cancel = () => {
clearTimeout(timer);
resolve(true);
};
});
wait.cancel = cancel;
return wait;
}
function doWork() {
const workFactor = Math.floor(600*Math.random());
const work = timeout(workFactor);
const result = work.then(canceled => {
if (canceled)
console.log('Work canceled');
else
console.log('Work done in', workFactor, 'ms');
return !canceled;
});
result.cancel = work.cancel;
return result;
}
function attemptWork() {
const work = doWork();
return Promise.race([work, timeout(300)])
.then(done => {
if (!done)
work.cancel();
return (done ? 'Work complete!' : 'I gave up');
});
}
attemptWork().then(console.log);
You can see from this one that the timeout's console.log is never executed when the timeout hits first. It should fail/succeed about half/half, for testing convenience.
Here's an easy example to understand the use of promise.race():
Imagine you need to fetch some data from a server and if the data takes too long to load (say 15 seconds) you want to show an error.
You would call promise.race() with two promises, the first being your ajax request and the second being a simple setTimeout(() => resolve("ERROR"), 15000)
Summary:
Promise.race is a JS built in function that accepts an iterable of Promises (e.g. Array) as an argument. This function then asynchronously returns a Promise as soon as one in of the Promises passed in the iterable is either resolved or rejected.
Example 1:
var promise1 = new Promise((resolve, reject) => {
setTimeout(() => resolve('Promise-one'), 500);
});
var promise2 = new Promise((resolve, reject) => {
setTimeout(() => resolve('Promise-two'), 100);
});
Promise.race([promise1, promise2]).then((value) => {
console.log(value);
// Both resolve, but promise2 is faster than promise 1
});
In this example first an array of Promises is passed in Promise.race. Both of the promises resolve but promise1 resolves faster. Therefore the promise is resolved with the value of promise1, which is the string 'Promise-one'.
Example 2:
const promise1 = new Promise((resolve, reject) => {
setTimeout(() => resolve('succes'), 2000);
});
const promise2 = new Promise((resolve, reject) => {
setTimeout(() => reject('err'), 1000);
});
Promise.race([promise1, promise2])
.then((value) => {
console.log(value);
}).catch((value) => {
console.log('error: ' + value);
});
In this second example the second promise rejects faster than the first promise can resolve. Therefore Promise.race will return a rejected promise with the value of 'err' which was the value that Promise2 rejected with.
The key point to understand is that Promice.race takes an iterable of Promises and returns a Promise based on the first resolved or rejected promise in that iterable (with the corresponding resolve() or reject() values).
Let's take an sample workaround of Promise.race like below.
const race = (promises) => {
return new Promise((resolve, reject) => {
return promises.forEach(f => f.then(resolve).catch(reject));
})
};
You can see race function executes all promises, but whomever finishes first will resolve/reject with wrapper Promise.

Reduced promise returns early

function timeout(delay) {
return new Promise((resolve, reject) => {
setTimeout(resolve, delay);
});
}
function printDots(delays) {
return delays.map((delay) => {
return timeout(delay).then(() => process.stdout.write('.'))
}).reduce((acc, prom) => acc.then(prom));
}
printDots([513, 3402, 1337, 4122]).then(() => process.stdout.write('DONE!'));
This prints .DONE!... but I expected that it would print ....DONE!
.then expects a function as it's arguments (onFullfilled, onRejected), any value that is not a function is totally ignored
However, prom in the reduce callback is a promise, so only the first promise will be waited for
simple change, marked below should fix things
function timeout(delay) {
return new Promise((resolve, reject) => {
setTimeout(resolve, delay);
});
}
function printDots(delays) {
return delays.map((delay) => {
return timeout(delay).then(() => process.stdout.write('.'))
}).reduce((acc, prom) => acc.then(() => prom));
// ^^^^^^
}
printDots([513, 3402, 1337, 4122]).then(() => process.stdout.write('DONE!'));
Note, however, that the promises will all be commenced at about the same time, no waiting for 0 to end before 1 starts etc
given this, an arguably better solution for printDots is to use Promise.all
function printDots(delays) {
return Promise.all(delays.map((delay) => timeout(delay).then(() => process.stdout.write('.'))));
}
As per comments, the promises need to run sequentially - that's as simple as only using reduce
function timeout(delay) {
return new Promise((resolve, reject) => {
setTimeout(resolve, delay);
});
}
function printDots(delays) {
return delays.reduce((acc, delay) => acc.then(() => timeout(delay).then(() => process.stdout.write('.'))), Promise.resolve());
}
printDots([513, 3402, 1337, 4122]).then(() => process.stdout.write('DONE!'));
In this case, you need to provide an initial promise (Promise.resolve) to reduce so that the first iteration is working with a promise like all subsequent ones are

Plain es6 promise; how to do n arbitrary operations?

I have two simple methods
// send a single command
sendCommand(command) {
return new Promise((resolve, reject) => {
this._commands.write(command, (response) => {
response === '?' : reject(command) : resolve(command);
});
});
}
// has to send multiple commands and wait for the result
sendArray(name, array) {
let bits = _.chunk(array, 4);
_.each(bits, (bit, index) => {
this.sendCommand(`QD ${name}[]${bits.join('\r')}\\`);
});
}
However, is there any way for this array be sent through the promises iteratively with plain es6 promises? Eg:
// for every bit in bits
this.sendCommand(bit1)
.then(() => { this.sendCommand(bit2) })
// ...
.then(() => { this.sendCommand(bitN) })
.catch(console.log);
Something like
let allBitsPromise = _.chunk(array, 4).reduce(function (p, bit) {
return p.then(() => sendCommand(bit));
}, Promise.resolve());
would work.
The least obvious part of this (to me, anyway) is that if the callback passed to then returns a promise p then the original promise is resolved when p is. So a promise like
Promise.resolve().then(() => {
return new Promise((resolve, reject) => setTimeout(resolve, 2000))
});
Is only resolved when the promise returned by the callback is resolved by setTimeout.
Keep a reference to the promise returned by the function and chain the .then call on it:
let promise = Promise.resolve();
_.each(bits, (bit, index) => {
promise = promise.then(() => this.sendCommand(`QD ${name}[]${bits.join('\r')}\\`));
});
promise.catch(error => console.log(error));
Note that this will send the data sequentially, which I assume is what you want.

Categories

Resources