Promise chaining unhandled promise - javascript

I'm learning Node.js right now and practicing using EventEmitter along with promises.
The successfulOrder function runs through both promises: verifyStockP and verifyCardP
both promises are super simple.
I noticed that when I purposely make one of the two promises reject, the program works as expected. The catch error code runs as expected.
If I force both promises to fail, I get the following error message:
'(node:2316) UnhandledPromiseRejectionWarning: card invalid;'
How would I make it so we don't get this error if both the verifyStockP and verifyCardP promises reject?
Thank you all in advance!
If you guys have any tips on something else I should fix on the code I'd be super grateful to hear it!
const EventEmitter = require('events');
const emitter = new EventEmitter();
const successfulOrder = (prod) => {
return console.log(`Your ${prod} order has processed!`)
}
const failedOrder = (prod) => {
return console.log(`Your ${prod} order has failed to process.`)
}
emitter.on('order_successful', successfulOrder);
emitter.on('order_failed', failedOrder);
const submitOrder = (prod, prodQuantity, userCardDigits) => {
const currentStock = 10;
const verifyStockP = new Promise((resolve, reject) => {
if(prodQuantity <= currentStock) {
resolve('stock available')
}
else {
reject('out of stock')
}
})
const verifyCardP = new Promise((resolve,reject) => {
let cardDigits = 16;
if (cardDigits === userCardDigits) {
resolve('card valid')
}
else {
reject('card invalid')
}
})
verifyStockP
.then((resolvedVal) => {
console.log(resolvedVal);
return verifyCardP
}).then((resolvedVal) => {
console.log('card valid')
emitter.emit('order_successful', prod)
}).catch((error) => {
console.log(error)
// emitter.emit('order_failed', prod)
})
}
submitOrder('sunglasses', 15, 17)

The issue is that if the first Promise rejects, the second Promise is never part of the Promise chain that has the catch
Two possible solutions ...
One way to handle this is separately add a "dummy" catch to the second promise
const p1 = new Promise((resolve, reject) => setTimeout(reject, 200, 'p1'));
const p2 = new Promise((resolve, reject) => setTimeout(reject, 100, 'p2'));
p1
.then(r => p2)
.catch(e => console.log('error', e));
p2.catch(e => console.log('error2', e));
You can see the result if first promise doesn't reject here
const p1 = new Promise((resolve, reject) => setTimeout(resolve, 200, 'p1'));
const p2 = new Promise((resolve, reject) => setTimeout(reject, 100, 'p2'));
p1
.then(r => p2)
.catch(e => console.log('error', e));
p2.catch(e => console.log('error2', e));
both catch blocks are run
If, however, the second Promise has no reliance at all on the first Promise, and in the code the way you wrote it means that the two Promises do run in parallel (as much as they can since you have no asynchrony in your code) - use Promise.all
const p1 = new Promise((resolve, reject) => setTimeout(reject, 100, 'p1'));
const p2 = new Promise((resolve, reject) => setTimeout(reject, 200, 'p2'));
Promise.all([p1, p2])
.then(([r1, r2]) => console.log(r1, r2))
.catch(e => console.log('error', e));

Related

Issue Promise.all()

I want to declare an array of empty promises
then resolve them manually, but it is not working ?
const tab = [1]
let promises = tab.map(e => new Promise(() => { }))
setTimeout(() => {
promises[0] = Promise.resolve()
}, 3000);
Promise.all(promises)
.then(e => console.log("finished"))
I DO NOT WANT to do it like this :
const tab = [1]
let promises = tab.map(e => new Promise((resolve) => {
setTimeout(() => {
resolve()
}, 3000);
}))
Promise.all(promises)
.then(e => console.log("finished"))
Thats the closest you can get:
const tab = [1]
let resolves = []
let promises = tab.map(e => new Promise((resolve) => {
resolves.push(resolve)
}))
setTimeout(() => {
resolves[0]()
}, 3000);
Promise.all(promises).then(e => console.log("finished"))
Here's a technique using a reusable deferred function that allows you to externally control a promise. We extend both resolve and reject functionality, allowing better control for the caller. This is a somewhat unconventional pattern but there are advantages to exploiting promises in this way. See this Q&A for an detailed example -
function deferred () {
let resolve, reject, promise = new Promise((res, rej) => {
resolve = res; reject = rej
})
return { promise, resolve, reject }
}
const tasks = [ deferred(), deferred(), deferred() ] // 3 "deferrred" objects
Promise.all(tasks.map(t => t.promise)).then(console.log, console.error)
setTimeout(_ => tasks[0].resolve("one"), 1000)
setTimeout(_ => tasks[1].resolve("two"), 2000)
setTimeout(_ => tasks[2].resolve("three"), 3000)

Parallel rxJs Observables without waiting for all to complete

Is there a way to request many similar requests in parallel using rxJs Observables in parallel without waiting for all requests to complete. I want to handle as each one comes. I don't want to use a loop of requests. Use case is one of the request is slow and there are many of them
I tried forkJoin (below) but it waits for all to complete. and mergeMap is not taking an array. Any help? Thanks
/*
// example of many posts
const post1 = getPostHTTP(1);
const post2 = getPostHTTP(2); // A slow post
const post3 = getPostHTTP(3);
...
*/
const promises = [];
for (let i = 0; i < 100; i++) {
promises.push(getPostHTTP(i))
}
forkJoin(promises).pipe(
finalize(() => {
//displayPost
})).subscribe(posts => {
posts.forEach((post: any) => {
displayPost(post)
})
})
I think that a mix of from and mergeMap is what you are looking for. The code could look like this
for (let i = 0; i < 100; i++) {
promises.push(getPostHTTP(i))
}
from(promises).pipe(
mergeMap(post => displayPost(post)),
).subscribe(
next: result => {// do something with the result of each displayPost operation},
error: err => {// handle errors},
complete: () => {// do something when everything is completed}
)
You should convert Promise to Observable via from (https://www.learnrxjs.io/learn-rxjs/operators/creation/from) and merge them into one Observable via merge (https://www.learnrxjs.io/learn-rxjs/operators/combination/merge).
It will be like:
merge(...promises.map(p => from(p))
.subscribe(post => {
// do smth with post
});
You can use Promise API to send multiple requests simultaneously and await them all to resolve.
If you have a condition that you need to run those promises sequentially, then this solution might not work for you.
Excerpt is from MDN.
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, 4000, 'four');
});
var p5 = new Promise((resolve, reject) => {
reject('reject');
});
Promise.all([p1, p2, p3, p4, p5]).then(values => {
console.log(values);
}, reason => {
console.log(reason)
});
//From console:
//"reject"
//You can also use .catch
Promise.all([p1, p2, p3, p4, p5]).then(values => {
console.log(values);
}).catch(reason => {
console.log(reason)
});

Promise.all.then is executed before the individual promises are completed

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.

Flat promise chain series with each promise call dependent upon resolution of prior call

What is the recommended method to flatten out a promise series so that subsequent promises are not run if the preceding promise does not resolve without nesting inside of the initial promises then statement?
I don't want to run promise 2 if promise 1 does not resolve.
var promise1 = new Promise((resolve, reject) =>{
reject('catch 1')
});
var promise2 = new Promise((resolve, reject) =>{
resolve('pass 2')
});
I want to do this:
var p1Resolved = promise1.then(result=>{
handleResult(result);
})
.catch(error => {
handleError(error);
})
if(p1Resolved)
{
promise2.then(result=>{
handleResult(result);
})
.catch(error => {
handleError(error);
})
}
I don't want to do this (nest promise2 inside of promise1 then):
promise1.then(result=>{
promise2.then(result=>{
handleResult(result);
})
.catch(error => {
handleError(error);
})
})
.catch(error => {
handleError(error);
})
turn promise2 into a function (may as well do promise1 as well, for consistency)
var promise1 = () => new Promise((resolve, reject) => reject('catch 1'));
var promise2 = () => new Promise((resolve, reject) => resolve('pass 2'));
promise1()
.then(result => promise2())
.then(result => console.log('done'))
.catch(reason => console.log('error', reason));

How to structure nested Promises

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);

Categories