The following sample code represents what am trying to do, and am stumped as to why the console.log inside the Promises.all.then() is completed before even the individual promises are completed. I am thinking that the map statement has something to do with it , may be, but am unsure.
My intention of using a map was to collect all successes and or failures and notify the user later on bulk operations.
var requests = []; var success=[]; var failures = [];
requests.push(new Promise((resolve, reject) => setTimeout(() => resolve("foo success"), 2000)));
requests.push(new Promise((resolve, reject) => setTimeout(() => reject("bar failure"), 1000)));
requests.push(new Promise((resolve, reject) => setTimeout(() => resolve("foo2 success"), 2000)));
requests.push(new Promise((resolve, reject) => setTimeout(() => reject("bar2 failure"), 1000)));
Promise.all(requests.map(p => {
p.then(r => {
console.log(r)
success.push(r);
});
p.catch((e) => { console.log(e); failures.push(e); });
})).then(function () { console.log("ALL promises completed")})
May i know what is wrong with the above code? Am i not implementing the promises as intended.?
It's because your requests.map(...) callback doesn't return anything, so you're mapping the Promises to undefined values. Therefore, you're awaiting an array of undefined values with the Promise.all call. Change to the following:
Promise.all(requests.map(p => {
p.then(r => {
console.log(r)
console.log("POSTed a record successfuly");
success.push(r);
});
p.catch((e) => { console.log(e); failures.push(e); });
return p;
})).then(function () { console.log("POSTED ALL")})
Update:
As #JoeFrambach pointed out, if you want the Promise.all to contain the contents of the original requests Promises, you'll want to return the original Promises. Since that's most likely what you want to do, I've updated my answer.
just return promise from your map callback function
var requests = []; var success=[]; var failures = [];
requests.push(new Promise((resolve, reject) => setTimeout(() => resolve("foo success"), 2000)));
requests.push(new Promise((resolve, reject) => setTimeout(() => reject("bar failure"), 1000)));
requests.push(new Promise((resolve, reject) => setTimeout(() => resolve("foo2 success"), 2000)));
requests.push(new Promise((resolve, reject) => setTimeout(() => reject("bar2 failure"), 1000)));
Promise.all(requests.map(p => {
p.then(r => {
console.log(r)
success.push(r);
});
p.catch((e) => { console.log(e); failures.push(e); });
return p; // ADD THIS LINE
})).then(function () { console.log("ALL promises completed")})
.catch(function() {console.log("FAILED PROMISE ALL")});
You need to return a promise within .map function.
Within .map function make last line as return p.
Promise all expects array of promises. You are returning array of undefined from map function so it immediately gets resolved.
Related
I currently have a few functions that return promises such as the below:
function action_one(){
return new Promise((resolve, reject)->{
...
});
}
I want to be able to wait on one before doing the next promise without nesting them. I had searched for some solutions, one being PromiseAll() but it does not seem to do it in order. Another solution was to do the following:
promise.then(result =>{
action
}.then(result =>{
action
}.then(result =>{
action
}.then(result =>{
action
}.then(result =>{ etc.
which I'm not sure works for my issue but it doesn't seem compactable.
what would be best practice in this situation?
Edit:
I'm not sure how to use the .then chain with multiple promises such as:
promise.then(result =>{
action
}promise.then(result =>{
action
}promise.then(result =>{
action
}promise.then(result =>{
action
}promise.then(result =>{ etc.
It does not yield the result I expect
I found my solution:
new Promise(function(resolve, reject) {
setTimeout(() => resolve(1), 1000);
}).then(function(result) {
alert(result); // 1
return new Promise((resolve, reject) => { // (*)
setTimeout(() => resolve(result * 2), 1000);
});
}).then(function(result) { // (**)
alert(result); // 2
return new Promise((resolve, reject) => {
setTimeout(() => resolve(result * 2), 1000);
});
}).then(function(result) {
alert(result); // 4
});
I needed to return the promise to chain them which is why it was not working.
Source: https://javascript.info/promise-chaining
I have removed error handling code as it is working (for simplicicty).
I have a function createOrder() which creates a new record in my MongoDB collection:
createOrder = order => new Promise((resolve, reject) => {
Order.create({orderId: order});
resolve();
});
getOrders() function that adds all current orders in the database to an array.
getOrders = _ => new Promise((resolve, reject) => {
Order.find({buyerId: curUser.userId}, (err, orders) => {
orders.map(order => order.status != 'cancelled' ? curUserOrders.push(order) : null);
});
resolve();
});
An external javascript file to post an order:
postOrder = () => {
window.location = `/new_order/order=${JSON.stringify(itemArray)}`;
}
and the node application that "gets" the request:
app.get("/new_order/order=:order", (req, res) => {
createOrder(JSON.parse(req.params.order))
.then(getOrders)
.then(_ => res.render("userprofile", {user : curUser, orders: curUserOrders}));
});
when i post a new order the postOrder() function is triggered and "userprofile" is displayed but none of the orders are displayed until i refresh the page again. Im still learning how promises work so im guessing the problem lies there. Please help :).
Promises are used for asynchronous operations, but your example resoves the promise right away.
createOrder = order => new Promise((resolve, reject) => {
Order.create({orderId: order});
resolve();
});
You would write the create function like:
createOrder(order).then(()=>{
// The asynchronous operation is completed!
})
But in your case it would happen the following:
createOrder = order => new Promise((resolve, reject) => {
//Start the asynchronous operation ....
Order.create({orderId: order});
//... but you just resolve the operation right away.
resolve();
});
A better example is probably the other function:
getOrders = _ => new Promise((resolve, reject) => {
Order.find({buyerId: curUser.userId}, (err, orders) => {
orders.map(order => order.status != 'cancelled' ? curUserOrders.push(order) : null);
});
// You just resolve without waiting for the callback of find.
resolve();
});
What you want:
getOrders = _ => new Promise((resolve, reject) => {
Order.find({buyerId: curUser.userId}, (err, orders) => {
orders.map(order => order.status != 'cancelled' ? curUserOrders.push(order) : null);
// Here we call resolve inside the callback and we wait for the find method and return the orders to the getOrders().then(orders => {}) call.
resolve(orders);
});
});
A Promise is what the name suggests, it provides you with a promise, that it will be fulfilled (resolved) at some point in the future. So you always want to wait with the resolve function until all operations are completed without errors.
Use it as callback like this
createOrder = order => new Promise((resolve, reject) => {
Order.create({orderId: order}).then(()=>{resolve});
});
I'm using Promise.all function to resolve the multiple promises concurrently. See the below code -
function check1() {
console.log('check 1 invoked')
return new Promise((resolve, reject) => {
setTimeout(()=> resolve('check1'), 4000);
})
}
function check2() {
console.log('check2 invoked')
return new Promise((resolve, reject) => {
setTimeout(()=> resolve('check2'), 4000);
})
}
var arr = [check1(), check2()];
Promise.all(arr)
.then((response) => console.log('response======>',response))
.catch((error) => console.error('error',error))
The problem with the above approach is that when I create the promise array, The respective functions gets invoked. I want to change the above code in the manner that two functions call only from the promise.all function.
Note - Need to store the promises functions in the array. Like I'm doing as var arr.
This should do it, do not make an array of promises but an array of functions, then map the functions to promises:
function check1() {
console.log('check 1 invoked')
return new Promise((resolve, reject) => {
setTimeout(()=> resolve('check1'), 4000);
})
}
function check2() {
console.log('check2 invoked')
return new Promise((resolve, reject) => {
setTimeout(()=> resolve('check2'), 4000);
})
}
//do not store the promises, store the funciton that creates a promise
var arr = [check1, check2];
Promise.all(
//map functions to promises
arr.map(fn=>fn())
)
.then((response) => console.log('response======>',response))
.catch((error) => console.error('error',error))
You can do in this way too -
var check1 = new Promise((resolve, reject) => {
setTimeout(()=> resolve('check1'), 4000);
});
var check2 = new Promise((resolve, reject) => {
setTimeout(()=> resolve('check2'), 4000);
});
Promise.all([check1, check2]).then(values => {
console.log(values);
}, reason => {
console.log(reason)
});
In other words, what happen if I do this:
const setBuildingPayload = new Promise((resolve, reject) => {
this.buildingForm.schema.forEach(() => {
setTimeout(() => {
resolve()
}, 3000)
})
})
Instead of this?
const setBuildingPayload = new Promise((resolve, reject) => {
return this.buildingForm.schema.map(() => {
return setTimeout(() => {
resolve()
}, 3000)
})
})
I ask this because I was surprised when using setBuildingPayload in Promise.all without return worked (the code inside Promise.all triggered after resolve().
And I thought every promise should return its value.
The function in question, (resolve, reject) => {...}, is called an executor function for the Promise. As per the last sentence in MDN docs for the executor
The return value of the executor is ignored
This can be demonstrated using this simple piece of code (modified from the example provided)
const setBuildingPayload = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("Done");
}, 1000);
return 100;
});
setBuildingPayload.then(result => {
console.log(result);
});
Notice how return 100 didn't make any difference. Hence, the returns in your second piece of code make no difference as well.
The return this.buildingForm.schema.map has no impact on how the Promise resolves. Its an unused return value and what our team likes to call "for funsies".
I have a situation where I think the only choice for me is to nest some Promises within each other. I have a Promise that needs to be performed and a method that does something until that Promise is complete. Something like this:
let promise = new Promise((resolve, reject) => {
// Do some stuff
});
doSomethingUntilPromiseisDone(promise);
However, within my Promise, I need to execute another method that returns another Promise:
let promise = new Promise((resolve, reject) => {
fetchValue(url)
.then((value) => {
// Do something here
}).catch((err) => {
console.error(err);
});
});
doSomethingUntilPromiseisDone(promise);
But now, in the fetchValue method's then statement, I have another method I need to execute that, guess what, returns another Promise:
let promise = new Promise((resolve, reject) => {
fetchValue(url)
.then((value) => {
saveToCache(value)
.then((success) => {
console.log('success!!');
resolve('success');
});
}).catch((err) => {
console.error(err);
});
});
doSomethingUntilPromiseisDone(promise);
So in the end, I have a Promise, within a Promise, within a Promise. Is there someway I can structure this better so that it is more straightforward? It seems like nesting them within each other is counter to Promise's intended chaining approach.
Use .then()
let doStuff = (resolve, reject) => {/* resolve() or reject() */};
let promise = new Promise(doStuff);
doSomethingUntilPromiseisDone(
promise
.then(value => fetchValue(url))
.then(value => value.blob())
.then(saveToCache)
)
.then(success => console.log("success!!"))
.catch(err => console.error(err))
you can use generator to flatten your nested promises (Bluebird.couroutine or Generators)
//Bluebird.couroutine
const generator = Promise.coroutine(function*() {
try {
const value = yield fetchValue(url);
const success = yield saveToCache(value);
console.log('success:', success);
} catch(e) {
console.error(err);
}
}));
generator();
Each function will call the next one with the result of the method before.
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);