I'm using some Promises to fetch some data and I got stuck with this problem on a project.
example1 = () => new Promise(function(resolve, reject) {
setTimeout(function() {
resolve('foo1');
}, 3000);
});
example2 = () => new Promise(function(resolve, reject) {
setTimeout(function() {
resolve('foo2');
}, 3000);
});
doStuff = () => {
const listExample = ['a','b','c'];
let s = "";
listExample.forEach((item,index) => {
console.log(item);
example1().then(() => {
console.log("First");
s = item;
});
example2().then(() => {
console.log("Second");
});
});
console.log("The End");
};
If I call the doStuff function on my code the result is not correct, the result I expected is shown below.
RESULT EXPECTED
a a
b First
c Second
The End b
First First
Second Second
First c
Second First
First Second
Second The End
At the end of the function no matter how I try, the variable s gets returned as "", I expected s to be "c".
It sounds like you want to wait for each Promise to resolve before initializing the next: you can do this by awaiting each of the Promises inside an async function (and you'll have to use a standard for loop to asynchronously iterate with await):
const example1 = () => new Promise(function(resolve, reject) {
setTimeout(function() {
resolve('foo1');
}, 500);
});
const example2 = () => new Promise(function(resolve, reject) {
setTimeout(function() {
resolve('foo2');
}, 500);
});
const doStuff = async () => {
const listExample = ['a','b','c'];
for (let i = 0; i < listExample.length; i++) {
console.log(listExample[i]);
await example1();
const s = listExample[i];
console.log("Fisrt");
await example2();
console.log("Second");
}
console.log("The End");
};
doStuff();
await is only syntax sugar for Promises - it's possible (just a lot harder to read at a glance) to re-write this without async/await:
const example1 = () => new Promise(function(resolve, reject) {
setTimeout(function() {
resolve('foo1');
}, 500);
});
const example2 = () => new Promise(function(resolve, reject) {
setTimeout(function() {
resolve('foo2');
}, 500);
});
const doStuff = () => {
const listExample = ['a','b','c'];
return listExample.reduce((lastPromise, item) => (
lastPromise
.then(() => console.log(item))
.then(example1)
.then(() => console.log("Fisrt"))
.then(example2)
.then(() => console.log('Second'))
), Promise.resolve())
.then(() => console.log("The End"));
};
doStuff();
If you want NOT to wait for each promise to finish before starting the next;
You can use Promise.all() to run something after all your promises have resolved;
example1 = () => new Promise(function(resolve, reject) {
setTimeout(function() {
resolve('foo1');
}, 3000);
});
example2 = () => new Promise(function(resolve, reject) {
setTimeout(function() {
resolve('foo2');
}, 3000);
});
doStuff = () => {
const listExample = ['a','b','c'];
let s = "";
let promises = []; // hold all the promises
listExample.forEach((item,index) => {
s = item; //moved
promises.push(example1() //add each promise to the array
.then(() => {
console.log(item); //moved
console.log("First");
}));
promises.push(example2() //add each promise to the array
.then(() => {
console.log("Second");
}));
});
Promise.all(promises) //wait for all the promises to finish (returns a promise)
.then(() => console.log("The End"));
return s;
};
doStuff();
How could I make the sortOrder function run once the getOrders function is fully completed?
I thought using a callback, so I expect getOrders to terminate and execute the sortOrder function, but I donĀ“t know how to do that. What should I do, any sugestions?
mounted () {
this.user = this.$q.localStorage.get.item('userInfo')
axios.get(`${api.getOrders}${this.user.cpf}`).then(response => {
this.orders = response.data
if (this.orders !== '') {
this.$q.loading.show()
this.getOrders(callback => {
this.sortOrder()
})
}
})
},
methods: {
getOrders: function () {
for (let i = 0; i < this.orders.length; i++) {
axios.get(api.obterOrderInfo(this.orders[i].orderId)).then(response => {
this.orderInfo = this.orderInfo.concat(response.data)
})
}
},
sortOrder: function () {
this.orderInfo.sort(this.compare)
this.$q.loading.hide()
},
compare: function (x, y) {
return x.creationDate < y.creationDate
}
}
getOrders: function () {
// Create array of requests
const requests = [];
for (let i = 0; i < this.orders.length; i++) {
requests.push(axios.get(api.obterOrderInfo(this.orders[i].orderId)))
}
// Map array of responses to orderInfo
return Promise.all(requests).then(results => this.orderInfo = results.map(result => result.data))
},
You'll need to wrap your promises together and solve them with a Promise.all like this:
getOrders: function () {
let promises = []
for (let i = 0; i < this.orders.length; i++) {
const promise = axios.get(api.obterOrderInfo(this.orders[i].orderId)).then(response => {
this.orderInfo = this.orderInfo.concat(response.data)
})
promises.push(promise)
}
Promise.all(promises)
.then(() => {
this.sortOrder()
})
},
var p1 = new Promise((resolve, reject) => {
setTimeout(resolve, 1000, 'one');
});
var p2 = new Promise((resolve, reject) => {
setTimeout(resolve, 2000, 'two');
});
var p3 = new Promise((resolve, reject) => {
setTimeout(resolve, 3000, 'three');
});
Promise.all([p1,p2,p3]).then(values => {
console.log(values);
}, reason => {
console.log(reason)
});
How can i wait for 2 promises to get completed ? Promise.race() wait for one promise to get completed.
Edit
I have n number of promises, what i want to achieve is wait for first k number of promises to get resolved and than trigger some event. assume k < n
Edit - 2
I am sure that k number of promise will be successfully resolved out of n numbers of promise given
(Note: Bluebird has a built-in helper function that serves precisely this purpose and has more or less the same behavior as my waitForN method below, so that option is always available)
I don't believe ES6 promises have an elegant built-in way to do this, but you could define the following relatively short now somewhat long helper function to take care of this.
Edit: Added an additional helper function that includes the indices of the successful promises in the result.
Edit: Updated to reject if the number of rejections reaches the point where successful resolution would be impossible.
Edit: Updated to more gracefully work with promises if it is iterable and check edge cases where the result should immediately resolve (e.g. if n === 0)
function waitForN(n, promises) {
let resolved = [];
let failCount = 0;
let pCount = 0;
return new Promise((resolve, reject) => {
const checkSuccess = () => {
if (resolved.length >= n) { resolve(resolved); }
};
const checkFailure = () => {
if (failCount + n > pCount) {
reject(new Error(`Impossible to resolve successfully. n = ${n}, promise count = ${pCount}, failure count = ${failCount}`));
}
};
for (let p of promises) {
pCount += 1;
Promise.resolve(p).then(r => {
if (resolved.length < n) { resolved.push(r); }
checkSuccess();
}, () => {
failCount += 1;
checkFailure();
});
}
checkFailure();
checkSuccess();
});
}
const waitForNWithIndices = (n, promises) =>
waitForN(n, promises.map((p, index) =>
Promise.resolve(p).then(result => ({ index, result }))
));
var p1 = new Promise((resolve, reject) => {
setTimeout(resolve, 1000, 'one');
});
var p2 = new Promise((resolve, reject) => {
setTimeout(resolve, 2000, 'two');
});
var p3 = new Promise((resolve, reject) => {
setTimeout(resolve, 3000, 'three');
});
var p4 = new Promise((resolve, reject) => {
setTimeout(resolve, 1500, 'four');
});
waitForN(2, [p1,p2,p3,p4]).then(values => {
console.log(values);
}, reason => {
console.log(reason)
});
waitForNWithIndices(2, [p1,p2,p3,p4]).then(values => {
console.log(values);
}, reason => {
console.log(reason)
});
You could use a higher order function to bake in the n then run all promises waiting for n to finish
var p1 = new Promise((resolve, reject) => {
setTimeout(resolve, 1000, 'one');
});
var p2 = new Promise((resolve, reject) => {
setTimeout(resolve, 2000, 'two');
});
var p3 = new Promise((resolve, reject) => {
setTimeout(resolve, 3000, 'three');
});
const raceN = n => (...promises) => {
const resolved = []
return new Promise((res, rej) =>
promises.forEach(promise =>
promise.then(x => {
resolved.push(x)
if (resolved.length === n)
res(resolved)
})
.catch(rej)
)
)
}
const race2 = raceN(2)
race2(p1, p2, p3)
.then(results => console.log(results))
race2(Promise.resolve('resolved'), Promise.reject('rejected'))
.then(results => console.log(results))
.catch(reason => console.log(reason))
Given my many comments on JLRishe's answer, I'll also post my subtly different version of the function:
function any(k, iterable) {
const results = [];
let fullfilled = 0, rejected = 0, n = 0;
return new Promise((resolve, reject) => {
k = Math.max(0, Math.floor(k));
function check() {
if (fulfilled == k)
resolve(results);
else if (rejected + k == n + 1)
reject(new Error(`No ${k} of ${n} got fulfilled`));
}
for (const thenable of iterable) {
const i = n++;
Promise.resolve(thenable).then(res => {
if (fulfilled++ < k) results[i] = res;
check();
}, () => {
rejected++;
check();
});
}
check();
});
}
A bit late but maybe this will do it for you:
const first = howMany => (promises,resolved=[],rejected=[]) => {
if(promises.length-rejected.length<howMany){
return Promise.reject([resolved,rejected]);
}
if(resolved.length===howMany){
return Promise.resolve(resolved);
}
if(resolved.length===0&&rejected.length===0){
promises=promises.map(
(p,index)=>Promise.resolve(p)
.then(resolve=>[resolve,index])
.catch(err=>Promise.reject([err,index]))
);
}
return Promise.race(promises)
.then(
([resolve,index])=>
first(howMany)(
promises.filter(
(p,i)=>i!==index
),
resolved.concat([resolve]),
rejected
)
)
.catch(
([err,index])=>
first(howMany)(
promises.filter(
(p,i)=>i!==index
),
resolved,
rejected.concat([err])
)
);
};
const first2 = first(2);
first2([p1,p2,p3])
.then(
result=>//you will have an array of 2 with resolve value(s)
)
.catch(
err=>//you will have 2 arrays, resolved ones and errors
)
EDIT: Updated to match question edits.
Pretty easy to roll your own when the abstraction limits your expressiveness.
This code is short and can easily be reworked to run in legacy ES3 environments.
You can also easily expand its usefulness by adding code to cancel unresolved timeouts. This is what you get when you don't hand everything over to a library; greater simplicity and more capability.
function raceN(n, fns, resolve, reject) {
const res = [], rej = [];
let halt = false;
for (const [idx, fn] of fns.entries()) {
fn(data => update(res, data, idx), data => update(rej, data, idx));
}
function update(arr, data, idx) {
if (halt) return;
arr.push({idx, data});
if ((halt=res.length >= n)) resolve(res);
else if ((halt=rej.length > fns.length - n)) reject(rej);
}
}
DEMO:
function raceN(n, fns, resolve, reject) {
const res = [], rej = [];
let halt = false;
for (const [idx, fn] of fns.entries()) {
fn(data => update(res, data, idx), data => update(rej, data, idx));
}
function update(arr, data, idx) {
if (halt) return;
arr.push({idx, data});
if ((halt=res.length >= n)) resolve(res);
else if ((halt=rej.length > fns.length - n)) reject(rej);
}
}
function rand() { return Math.ceil(Math.random() * 5000) }
var fns = [(resolve, reject) => {
setTimeout(resolve, rand(), 'one');
},
(resolve, reject) => {
setTimeout(resolve, rand(), 'two');
},
(resolve, reject) => {
setTimeout(reject, rand(), 'three');
}];
raceN(2, fns, values => {
console.log("SUCCESS:", values);
}, reason => {
console.log("REJECT:", reason)
});
If you're guaranteed that at least n elements will succeed, it then becomes even shorter and simpler.
function raceN(n, fns, resolve, reject) {
const res = [];
for (const [idx, fn] of fns.entries()) {
fn(data => res.length < n && res.push({idx, data}) == n) && resolve(res),
data => reject({idx, data}));
}
}
DEMO:
function raceN(n, fns, resolve, reject) {
const res = [];
for (const [idx, fn] of fns.entries()) {
fn(data => res.length < n && res.push({idx, data}) == n) && resolve(res),
data => reject({idx, data}));
}
}
function rand() { return Math.ceil(Math.random() * 4000) }
var fns = [(resolve, reject) => {
setTimeout(resolve, rand(), 'one');
},
(resolve, reject) => {
setTimeout(resolve, rand(), 'two');
},
(resolve, reject) => {
setTimeout(reject, rand(), 'three');
}];
raceN(2, fns, values => {
console.log("SUCCESS:", values);
}, reason => {
console.log("REJECTING:", reason)
});
One other idea could be using Promise.reject() to cut it short when the condition is met while catching possible errors thrown, could be as follows;
var p1 = new Promise((v, x) => setTimeout(v, 1000, 'one')),
p2 = new Promise((v, x) => setTimeout(v, 5000, 'two')),
p3 = new Promise((v, x) => setTimeout(v, 1500, 'three')),
pn = (n, ps, k = 0, r = []) => Promise.all(ps.map(p => p.then(v => (k === n - 1 ? Promise.reject(r.concat(v))
: (++k, r.push(v))))))
.catch(r => Array.isArray(r) ? r : Promise.reject(r));
pn(2,[p1,p2,p3]).then(rs => console.log(rs))
.catch(e => console.log(e));
A more modern approach, in 2022, using iter-ops library:
import {pipeAsync, take, waitRace} from 'iter-ops';
// easy-to-read way to define a job function:
const job = (delay, value) => new Promise(resolve => {
setTimeout(() => resolve(value), delay);
});
// list of jobs:
const input = [
job(1000, 'one'),
job(2000, 'two'),
job(3000, 'three')
];
const i = pipeAsync(
input, // our input
waitRace(10), // race-resolve with cache up to 10 items
take(2) // take only the first 2 items
).catch(err => {
console.log(err); // report any iteration-time error
});
// iterating through our list of jobs:
(async function () {
for await(const a of i) {
console.log(a);
}
})();
This will output one and two. You can twist the delays to see the result change accordingly.
CREDITS
I am the author of the library, while operator waitRace was implemented by #Bergi.