I wonder if there is a way that I can execute an array of promises waterfally? i.e. I want the next promise not to start until the current promise is resolved.
My test:
import Promise from 'bluebird'
describe('promises waterfall', () => {
const result = []
function doItLater(val, time = 50) => {
return new Promise(resolve => {
setTimeout(() => {
result.push(val)
resolve(val)
}, time)
})
}
it('execute promises one after one resolved', done => {
result.push(1)
const promiseList = [doItLater('a', 100),doItLater('b', 1),doItLater('c')]
result.push(2)
Promise.each(
promiseList,
(output) => {
result.push(output + '-outputted')
}
)
.then(
() => {
result.push(3)
console.log(result)
expect(result).to.eql([ 1, 2, 'a', 'b', 'c', 'a-outputted', 'b-outputted', 'c-outputted', 3 ])
done()
}
)
})
})
Update
Sorry if I have made it too confusing. I'm asking how I can make my test pass. Currently my test fails - the actual result is in wrong order:
[ 1,
2,
'b',
'c',
'a',
'a-outputted',
'b-outputted',
'c-outputted',
3 ]
When you create promises like this
[doItLater('a', 100),doItLater('b', 1),doItLater('c')]
they are already executing asynchronously. There is no way of controlling their order of execution.
You can use Promise.reduce for your case, like this
Promise.reduce([['a', 100], ['b', 1], ['c']], function(res, args) {
return doItLater.apply(null, args).then(function(output) {
res.push(output + '-outputted');
return res;
});
}, result)
.then(function(accumulatedResult) {
....
});
Now, we are creating promises one by one, after the previous promise is resolved and we are accumulating the result in res (which is actually result). When all the promises are resolved, the accumulatedResult will have all the values.
Note: It is strongly recommended NOT to share data between functions. Please make sure that you have compelling reasons to do so, if you have to do.
// Promisse waterfall pattern
var promises = [1,2,3].map((guid)=>{
return (param)=> {
console.log("param", param);
var id = guid;
return new Promise(resolve => {
// resolve in a random amount of time
setTimeout(function () {
resolve(id);
}, (Math.random() * 1.5 | 0) * 1000);
});
}
}).reduce(function (acc, curr, index) {
return acc.then(function (res) {
return curr(res[index-1]).then(function (result) {
console.log("result", result);
res.push(result);
return res;
});
});
}, Promise.resolve([]));
promises.then(console.log);
Related
I am trying to understand Promise.all here.
What I did here was to covert below code using Promise.all to achieve the same result.
I understand that Promise all combine the data1, data2.
My question here is that how does Promise.All work without resolve method?
Does Promise resolve those data within the method itself?
Please advise.
const readAllUsersChaining = () => {
return new Promise((resolve, reject) => {
let result = [];
getDataFromFilePromise(user1Path)
.then((data) => {
result.push(JSON.parse(data)); // what are you doing? he's gone mad...
return getDataFromFilePromise(user2Path);
})
.then((data) => {
result.push(JSON.parse(data));
result ? resolve(result) : reject(result);
});
});
};
const readAllUsers = () => {
const data1 = getDataFromFilePromise(user1Path);
const data2 = getDataFromFilePromise(user2Path);
console.log(data1, data2);
return Promise.all([data1, data2]).then((data) => {
return data.map((el) => JSON.parse(el));
});
};
My question here is that how does Promise.All work without resolve method?
Not quite sure what you mean. Promise.all simply creates a new promise internally that is resolved when all other promises are resolved.
Here is a simple implementation of Promise.all for the case that arguments are always promises:
function all(promises) {
if (promises.length === 0) {
return Promise.resolve([]);
}
return new Promise((resolve, reject) => {
const results = [];
let resolved = 0;
promises.forEach((promise, i) => {
promise.then(
result => {
results[i] = result;
resolved++;
if (resolved === promised.length) {
resolve(results);
}
},
error => reject(error)
);
});
}
Promise.all allows to wait until all promise passed as arguments to be fullfilled before the then method attach to be execute.
The real use case would be when you have to perform five call to an API, an you want to perform some treatement only when you have get the data from all the API call. you can rely on the Promise.all function which will wait all passed promised to be fullfilled for it to be fullfiled on it turn.
Bellow I provide an example of a Promise.all call. which has two Promises passed as argument. the first one has a timer which fullfilled it after 5 second and the second if fullfiled immediately but. the Promise.all will be fullfilled only when both of the promise passed as argument ar fullfilled
const firstPromise = new Promise((resolve, reject) => {
setTimeout(()=> {
return resolve({
name: "First Promise"
});
}, 5000);
});
const secondPromise = new Promise((resolve, reject) => {
return resolve({
name: "Second Promise"
});
})
const all = Promise.all([firstPromise, secondPromise]).then((response) => {
console.log(response);
});
I have a dynamic number of promises that I need to run sequentially.
I understood how I can run sequentially promises but I don't succeed to make it dynamic with a number of promises that could vary.
Here is a way I found to do it statically How to resolve promises one after another? :
function waitFor(timeout) {
return new Promise(function(resolve, reject) {
setTimeout(function() {
resolve(`Finished waiting ${timeout} milliseconds`);
}, timeout);
});
}
waitFor(1000).then(function(result) {
$('#result').append(result+' # '+(new Date().getSeconds())+'<br>');
return waitFor(2000);
}).then(function(result) {
$('#result').append(result+' # '+(new Date().getSeconds())+'<br>');
return waitFor(3000);
}).then(function(result) {
$('#result').append(result+' # '+(new Date().getSeconds())+'<br>');
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div id="result"></div>
I would like to do the same but instead of 3 nested promises, I would like to have any number I want.
Can you help me ?
Thanks a lot!!
There are three basic ways to achieve this task with Promises.
.reduce() pattern.
function waitFor(timeout) {
return new Promise(function(resolve, reject) {
setTimeout(function() {
resolve(`Finished waiting ${timeout} milliseconds`);
}, timeout);
});
}
var timeouts = [1000, 2000, 2000, 3000, 1000],
sequence = tos => tos.reduce((p,c) => p.then(rp => waitFor(c))
.then(rc => console.log(`${rc} # ${new Date().getSeconds()}`)), Promise.resolve());
sequence(timeouts);
The recursive pattern.
function waitFor(timeout) {
return new Promise(function(resolve, reject) {
setTimeout(function() {
resolve(`Finished waiting ${timeout} milliseconds`);
}, timeout);
});
}
var timeouts = [1000, 2000, 2000, 3000, 1000],
sequence = ([to,...tos]) => to !== void 0 && waitFor(to).then(v => (console.log(`${v} # ${new Date().getSeconds()}`), sequence(tos)));
sequence(timeouts);
Scan from left pattern.
The scanl pattern would sequence promises one after another but once it is completed you also have access to the interim promise resolutions. This might be useful in some cases. If you are going to construct an asynchronous tree structure lazily (branching from the nodes only when needed) you need to have access to the previous promise resolutions.
In order to achieve scanl functionality in JS, first we have to implement it.
var scanl = (xs, f, acc) => xs.map((a => e => a = f(a,e))(acc))
we feed scanl with xs which is the array of timeouts in this particular example, f which is a callback function that takes acc (the accumulator) and e (current item) and returns the new accumulator. Accumulator values (the interim promise resolutions) are mapped over the timeouts array to be accessed when needed.
function waitFor(timeout) {
return new Promise(function(resolve, reject) {
setTimeout(function() {
resolve(`finished waiting ${timeout} milliseconds`);
}, timeout);
});
}
var timeouts = [1000, 2000, 2000, 3000, 1000],
scanl = (xs, f, acc) => xs.map((a => e => a = f(a,e))(acc)),
proms = scanl(timeouts, // input array
(a,t,r) => a.then(v => (r = v, waitFor(t))) // callback function
.then(v => (console.log(`${r} and ${v}`),
`${r} and ${v}`)),
Promise.resolve(`Started with 0`)); // accumulator initial value
// Accessing the previous sub sequential resolutions
Promise.all(proms)
.then(vs => vs.forEach(v => console.log(v)));
.as-console-wrapper {
max-height: 100% !important
}
Make a seprate function to handle the number of iterations
function waitFor(timeout) {
return new Promise(function(resolve, reject) {
setTimeout(function() {
resolve(`Finished waiting ${timeout} milliseconds`);
}, timeout);
});
}
function resultHandler(result) {
$('#result').append(result+' # '+(new Date().getSeconds())+'<br>');
return waitFor(2000);
}
function repeat(promise,num){
if(num>0)
repeat(promise.then(resultHandler),num-1);
}
repeat(waitFor(1000),2)
Forget I commented (when you convert a Promise to Observable or include the promise in an array, the Promise is executed). You can use a "recursive" function
foolPromise(index: number, interval: number) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({ id: index, data: new Date().getTime() % 100000 });
}, interval);
})
}
intervals: number[] = [1000, 50, 500];
recursive(req: any, index: number) {
req.then(res => {
console.log(res);
index++;
if (index < this.intervals.length)
this.recursive(this.foolPromise(index, this.intervals[index]), index);
})
}
ngOnInit() {
this.recursive(this.foolPromise(0, this.intervals[0]), 0)
}
If you don't care for serialization, you can use Promise.all https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all
Promise.all([promise1, promise2, promise3]).then(function(values) {
// do something with values
}).catch(function(err) {
// error called on first failed promise
});
Or, you can use an async function:
async function doSomething(arrayOfPromises) {
for (const item of arrayOfPromises) {
const result = await item;
// now do something with result
}
};
How do I wrap this routine inside a Promise so that I only resolve when I get all the data?
var accounts = [];
getAccounts(userId, accs => {
accs.forEach(acc => {
getAccountTx(acc.id, tx => {
accounts.push({
'id': acc.id,
'tx': tx
});
});
})
});
EDIT: Any issues if I do it like this?
function getAccountsAllAtOnce() {
var accounts = [];
var required = 0;
var done = 0;
getAccounts(userId, accs => {
required = accs.length;
accs.forEach(acc => {
getAccountTx(acc.id, tx => {
accounts.push({
'id': acc.id,
'tx': tx
});
done = done + 1;
});
})
});
while(done < required) {
// wait
}
return accounts;
}
Let's put this routine into a separate function, so it is easier to re-use it later. This function should return a promise, which will be resolved with array of accounts (also I'll modify your code as small as possible):
function getAccountsWithTx(userId) {
return new Promise((resolve, reject) => {
var accounts = [];
getAccounts(userId, accs => {
accs.forEach(acc => {
getAccountTx(acc.id, tx => {
accounts.push({
'id': acc.id,
'tx': tx
});
// resolve after we fetched all accounts
if (accs.length === accounts.length) {
resolve(accounts);
}
});
});
});
});
}
The single difference is just returning a promise and resolving after all accounts were fetched. However, callbacks tend your codebase to have this "callback hell" style, when you have a lot of nested callbacks, and it makes it hard to reason about it. You can workaround it using good discipline, but you can simplify it greatly switching to returning promises from all async functions. For example your func will look like the following:
function getAccountsWithTx(userId) {
getAccounts(userId)
.then(accs => {
const transformTx = acc => getAccountTx(acc.id)
.then(tx => ({ tx, id: acc.id }));
return Promise.all(accs.map(transformTx));
});
}
Both of them are absolutely equivalent, and there are plently of libraries to "promisify" your current callback-style functions (for example, bluebird or even native Node util.promisify). Also, with new async/await syntax it becomes even easier, because it allows to think in sync flow:
async function getAccountsWithTx(userId) {
const accs = await getUserAccounts(userId);
const transformTx = async (acc) => {
const tx = getAccountTx(acc.id);
return { tx, id: acc.id };
};
return Promise.all(accs.map(transformTx));
}
As you can see, we eliminate any nesting! It makes reasoning about code much easier, because you can read code as it will be actually executed. However, all these three options are equivalent, so it is up to you, what makes the most sense in your project and environment.
I'd split every step into its own function, and return a promise or promise array from each one. For example, getAccounts becomes:
function getAccountsAndReturnPromise(userId) {
return new Promise((resolve, reject) => {
getAccounts(userId, accounts => {
return resolve(accounts);
});
});
};
And getAccountTx resolves to an array of { id, tx } objects:
function getAccountTransactionsAndReturnPromise(accountId) {
return new Promise((resolve, reject) => {
getAccountTx(account.id, (transactions) => {
var accountWithTransactions = {
id: account.id,
transactions
};
return resolve(accountWithTransactions);
});
});
};
Then you can use Promise.all() and map() to resolve the last step to an array of values in the format you desire:
function getDataForUser(userId) {
return getAccountsAndReturnPromise(userId)
.then(accounts=>{
var accountTransactionPromises = accounts.map(account =>
getAccountTransactionsAndReturnPromise(account.id)
);
return Promise.all(accountTransactionPromises);
})
.then(allAccountsWithTransactions => {
return allAccountsWithTransactions.map(account =>{
return {
id: account.id,
tx: tx
}
});
});
}
I am building a javascript object where some of the values are defined by asynchronous functions. My problem is the object gets defined faster than the asynchronous functions can return values:
const array = ['a', 'b', 'c', 'd']
const myArrayObj = [];
function returnKey1 () {
// make async call then store it in the return key1val:
return key1val
}
function returnKey2 () {
// make async call then store it in the return key2val:
return key2val
}
function returnKey3 () {
// make async call then store it in the return key3val:
return key3val
}
_.forEach( array, function ( arr ) {
myArrayObj.push({
key1: returnKey1(), // returns undefined
key2: returnKey2(), // returns undefined
key3: returnKey3(), // returns undefined
});
});
Does anyone know the correct way I should be doing this? Thanks in advance!
The nature of asynchronicity is that you must wait for an async process to finish if you want to access its end result.
In your case, you could achieve this using promises with not a lot of code.:
// promise that resolves after 2 seconds
const timeoutPromise = (str) => new Promise(resolve => setTimeout(() => resolve(str), 2000));
// functions that return promises that will eventually resolve to appropriate key values
function returnKey1() {
return timeoutPromise('key3');
}
function returnKey2() {
return timeoutPromise('key2');
}
function returnKey3() {
return timeoutPromise('key3');
}
// helper function that returns a promise which will resolve with all the keys when all key-returning promises resolve
function getKeys() {
return Promise.all([
returnKey1(),
returnKey2(),
returnKey3()
])
}
// usage
getKeys().then((keys) => {
console.log(
keys[0],
keys[1],
keys[2]
);
});
The old-school approach would be to use callbacks instead of promises, which have larger browser support but are much cruder and primitive.
Note: With modern transpilers and/or promise libraries you can obtain wide browser support for promises as well.
// function that calls its callback after 2 seconds
const timeoutCallback = (cb, key) => setTimeout(() => cb(key), 2000);
// functions that eventually call their callbacks with appropriate key values
function returnKey1(cb) {
return timeoutCallback(cb, 'key1');
}
function returnKey2(cb) {
return timeoutCallback(cb, 'key2');
}
function returnKey3(cb) {
return timeoutCallback(cb, 'key3');
}
// helper function that calls its callback when all the keys are obtained
function getKeys(cb) {
let keys = [undefined, undefined, undefined];
let hasAllKeys = () => keys.every(key => typeof key === 'string');
function makeReturnKeyCallback(idx) {
return (key) => {
keys[idx] = key;
if (hasAllKeys()) {
cb(keys);
}
};
}
returnKey1(makeReturnKeyCallback(0));
returnKey2(makeReturnKeyCallback(1));
returnKey3(makeReturnKeyCallback(2));
}
// usage
getKeys((keys) => {
console.log(
keys[0],
keys[1],
keys[2]
);
});
Whatever you do will end up using promises. You can use Promise.all directly, or if you are willing to spend a bit more time you could write your own version of Promise.all for objects; finally, you could do this using async functions, which are a way to write promise-based code in a way which looks sort of synchronous.
Using Promise.all:
// Just examples for testing purposes.
function returnKey1() { return promise(); }
function returnKey2() { return promise(); }
function returnKey3() { return promise(); }
// Make a promise returning a random value after random time.
function promise() {
return new Promise(resolve => {
const random = Math.random() * 1000;
setTimeout(() => resolve(random), random);
});
}
// Wait for all promises to finish, then construct result.
Promise.all([returnKey1(), returnKey2(), returnKey3()])
.then(([key1, key2, key3]) => ({key1, key2, key3}))
.then(console.log);
Writing your own version of Promise.all for objects
We could also create an analog to Promise.all for objects. Some libraries have such a routine. We will call it promiseAllKeys. We will pass it an object each of whose property values in a promise; it returns a promise for an object with all the values filled in. This saves us the trouble of converting an array coming back from Promise.all into the desired object.
function returnKey1() { return promise(); }
function returnKey2() { return promise(); }
function returnKey3() { return promise(); }
function promise() {
return new Promise(resolve => {
const random = Math.random() * 1000;
setTimeout(() => resolve(random), random);
});
}
// Convenience routine to construct an object from arrays of keys and values.
function objectFromKeysAndValues(keys, values) {
const result = {};
for (const i = 0; i < keys.length; i++) result[keys[i]] = values[i];
return result;
}
function promiseAllKeys(promises) {
return Promise.all(Object.values(promises))
.then(values => objectFromKeysAndValues(Object.keys(promises), values));
}
promiseAllKeys({key1: returnKey1(), key2: returnKey2(), key3: returnKey3()})
.then(console.log);
Using async/await
You could simplify your code using async functions. However, this code as written will wait until each promise completes before running the next one.
function returnKey1() { return promise(); }
function returnKey2() { return promise(); }
function returnKey3() { return promise(); }
function promise() {
return new Promise(resolve => {
const random = Math.random() * 1000;
setTimeout(() => resolve(random), random);
});
}
async function makeObject() {
return {key1: await returnKey1(), key2: await returnKey2(), key3: await returnKey3()};
}
makeObject().then(console.log);
Simple but messy:
const array = ['a', 'b', 'c', 'd']
const myArrayObj = [];
function returnKey1 (cb) {
// make async call then store it in the return key1val:
cb ("key1val");
}
function returnKey2 (cb) {
// make async call then store it in the return key2val:
cb("key2val");
}
function returnKey3 (cb) {
// make async call then store it in the return key3val:
cb("key3val");
}
_.forEach( array, function ( arr ) {
var o ={};
returnKey1(function(key){
o.key1=key;
returnKey2(function(key){
o.key2=key;
returnKey3(function(key){
o.key3=key
myArrayObj.push(obj);
})
})
})
});
});
I'm using babel to transpile my node.js#0.10.x code and I'm stuck with promises.
I need allSettled-type functionality that I could use in q and bluebird or angular.$q for example.
On babel's core-js Promise, there is no allSettled method.
Currently I'm using q.allSettled as a workaround:
import { allSettled } from 'q';
Is there something like that in babel polyfill? Alternatively, which is a good algorithm for me to try to implement?
2019 Answer
There was a proposal to add this function to the ECMAScript standard, and it has been accepted! Check out the Promise.allSettled docs for details.
Original Answer
If you take a look at the implementation of q.allSettled you'll see it's actually quite simple to implement. Here's how you might implement it using ES6 Promises:
function allSettled(promises) {
let wrappedPromises = promises.map(p => Promise.resolve(p)
.then(
val => ({ status: 'fulfilled', value: val }),
err => ({ status: 'rejected', reason: err })));
return Promise.all(wrappedPromises);
}
2020 answer:
What the other answers are trying to do is to implement Promise.allSettled themselves. This was already done by the core-js project.
What you need to do is to make babel polyfill Promise.allSettled for you via core-js. The way you configure it to do so is through #babel/preset-env like so:
presets: [
['#babel/preset-env', {
useBuiltIns: 'usage',
corejs: {version: 3, proposals: true},
}],
],
In your build artifact this will add a call to require("core-js/modules/esnext.promise.all-settled") which monkeypatches the .allSettled function to the promises API.
const allSettled = promises =>
Promise.all(promises.map(promise => promise
.then(value => ({ state: 'fulfilled', value }))
.catch(reason => ({ state: 'rejected', reason }))
));
Or if you insist on polyfilling it:
if (Promise && !Promise.allSettled) {
Promise.allSettled = function (promises) {
return Promise.all(promises.map(function (promise) {
return promise.then(function (value) {
return { state: 'fulfilled', value: value };
}).catch(function (reason) {
return { state: 'rejected', reason: reason };
});
}));
};
}
Taken from here
Alternatively, which is a good algorithm for me to try to implement?
create a new promise with an executor function
use a counter/result array in the scope of the executor
register a then() callback with each parent promise saving the results in the array
resolve/reject promise from step 1 when counter indicates that all parent promises are done
Here's my attempt at something similar, I have Newsletter service and in my case I wanted my allSettled promise to resolve with an array of all the results (rejections and resolutions), IN ORDER, once all the email_promises are settled (all the emails had gone out):
Newsletter.prototype.allSettled = function(email_promises) {
var allSettledPromise = new Promise(function(resolve, reject) {
// Keep Count
var counter = email_promises.length;
// Keep Individual Results in Order
var settlements = [];
settlements[counter - 1] = undefined;
function checkResolve() {
counter--;
if (counter == 0) {
resolve(settlements);
}
}
function recordResolution(index, data) {
settlements[index] = {
success: true,
data: data
};
checkResolve();
}
function recordRejection(index, error) {
settlements[index] = {
success: false,
error: error
};
checkResolve();
}
// Attach to all promises in array
email_promises.forEach(function(email_promise, index) {
email_promise.then(recordResolution.bind(null, index))
.catch(recordRejection.bind(null, index));
});
});
return allSettledPromise;
}
my implementation will be below
Promise.prototype.myAllSettled = function (arr = []) {
return new Promise(function processIterable(resolve, reject) {
let result = [];
arr.forEach((item) => {
item
.then((value) => {
result.push({ status: "fulfilled", value: value });
if (arr.length === result.length) resolve(result);
})
.catch((err) => {
result.push({ status: "rejected", reason: `${err}` });
if (arr.length === result.length) resolve(result);
});
});
});
};
Here's another take at the same functionality: spex.batch
The source code would be too much to re-post here, so here's just an example from the batch processing of how to use it:
var spex = require('spex')(Promise);
// function that returns a promise;
function getWord() {
return Promise.resolve("World");
}
// function that returns a value;
function getExcl() {
return '!';
}
// function that returns another function;
function nested() {
return getExcl;
}
var values = [
123,
"Hello",
getWord,
Promise.resolve(nested)
];
spex.batch(values)
.then(function (data) {
console.log("DATA:", data);
}, function (reason) {
console.log("REASON:", reason);
});
This outputs:
DATA: [ 123, 'Hello', 'World', '!' ]
Now let's make it fail by changing getWord to this:
function getWord() {
return Promise.reject("World");
}
Now the output is:
REASON: [ { success: true, result: 123 },
{ success: true, result: 'Hello' },
{ success: false, result: 'World' },
{ success: true, result: '!' } ]
i.e. the entire array is settled, reporting index-bound results.
And if instead of reporting the entire reason we call getErrors():
console.log("REASON:", reason.getErrors());
then the output will be:
REASON: [ 'World' ]
This is just to simplify quick access to the list of errors that occurred.