Issue Promise.all() - javascript

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)

Related

Promise chaining unhandled promise

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

Possible implementation of Promise.any on JS

In Promise.race the promise returns as soon that the primary promise returns. In Promise.all returns when all promises resolves, but lasts one problem. If any of all promises rejects all others will be rejected.
Instead of it, exists a proposal for a Promise.any, the returns every promise alone, independent of each other, short-circuiting on a rejection.
const logAfterWait = (seconds) => new Promise((resolve, reject) => {
return setTimeout(() => resolve(console.log(`${time} time passed`)), seconds)
})
const watingList = [
logAfterWait(convertToSeconds(10)),
logAfterWait(convertToSeconds(30)),
logAfterWait(convertToSeconds(5))
]
const logReading = async (fn) => {
console.log(`${time}: reading file`)
await fn()
}
const readFiles = (files) => Promise.all(watingList.map(logReading))
.catch((error) => new Error(error))
The problem here is the block of event loop on the maping cause block on event loop on Promise.all, returning every results on the same time, differ from the expected result, that is, 5, 10, 30 seconds.
Can I avoid this situation on waitingList.map?
You can leverage the fact that Promise.race forms a monoid by creating a Promise that never settles:
const empty = x => new Promise((res, rej) => x); // never settling promise
const ps = [
Promise.reject(1).catch(empty),
Promise.resolve(2).catch(empty),
Promise.resolve(3).catch(empty)];
Promise.race(ps)
.then(console.log); // 2
You need to attach a catch handler to each Promise in the array though. You can probably create a utility function that does this for you.
You could think of something like this:
// a solution might just be not using async/await
const any = (promises) => new Promise((resolve, reject) => {
let errors = [];
let resolved;
const onFulfill = (value) => {
// skip if already resolved
if (resolved) { return; }
resolved = true;
// resolve with the first available value
resolve(value);
};
const onError = (error) => {
// skip if already resolved
if (resolved) { return; }
// collect error
errors = errors.concat(error);
// reject promise combinator if all promises are failed
if (errors.length === promises.length) {
reject(errors);
}
};
return promises.forEach((promise) => promise.then(
onFulfill,
onError,
));
});
const sleep = (ms) => new Promise(r => setTimeout(() => r(ms), ms));
const err = (ms) => sleep(ms).then(() => Promise.reject(ms));
// it would log 2000, since it is the first to resolve
any([sleep(3000), err(100), sleep(2000)]).then(console.info)
// it would an array of 2 failures
any([err(50), err(60)]).catch(console.error)
the block of IO
Note that there isn't any block of IO in javascript, the thread is just free to tackle any other task while waiting for the promises to be resolved.
Consequently, I came to a conclusion. We create a resolver that is an Either monad(not a pure implementation of the Either monad) that returns [err, response] over a map function.
The catch blocks are necessary to avoid the Unhandled Promise Rejection Warning.
const time = () => `${new Date().getHours()}:${new Date().getMinutes()}:${new Date().getSeconds()}`;
const sleep = (ms, pNumber) => new Promise((resolve, reject) => {
return pNumber < 3
? setTimeout(() => resolve(console.log(`${time()} time passed`)), ms)
: reject(null)
}).catch(null)
Promise.prototype.resolver = async (promise) => {
this._result = await Promise.all([promise])[0];
return this._result == null
? ["The time flies", promise]
: [null, promise]
}
const watingList = [
Promise.resolver(sleep(0, 0).catch(console.error)),
Promise.resolver(sleep(3000, 1).catch(console.error)),
Promise.resolver(sleep(5000, 2).catch(console.error)),
Promise.resolver(sleep(5000, 3).catch(console.error))
]
const logReading = (list) => {
return list.map(p => p.then(console.log(`${time()}: reading file`))
.catch(console.log))
}
((read) => logReading(read))(watingList)
PS: time function differs from the expected because of the evaluate time.
Resources can be found here:
1 - https://frontendmasters.com/courses/hardcore-js-v2/either-monad/

Chained promises in Promise.all without wrapper Promise

Is it possible for Promise.all to return the last value of the chain without a wrapper promise?
Without using await, it doesn't work in my context
Without wrapper example :
function sum1(x){
return new Promise(resolve => {
setTimeout(t => resolve(x+1),3000)
})
}
const p1 = sum1(1);
p1
.then(sum1)
.then(sum1)
Promise.all([p1])
.then(v => console.log(v[0]));
It logs 2 instead of the expected 4.
But if I use a wrapper it works :
function sum1(x){
return new Promise(resolve => {
setTimeout(t => resolve(x+1),3000)
})
}
function sum3(x){
return sum1(x)
.then(sum1)
.then(sum1)
}
const p2 = sum3(1);
Promise.all([p2])
.then(v => console.log(v[0]));
But in my context it gets complicated if I need to create and name a wrapper function for every chain of promises...
Is this possible?
You can store the value returned by p1.then(sum1).then(sum1) and call Promise.all on this value. It waits for the resolution of the promise chain not only the first one. Here is an example:
function sum1(x) {
return new Promise(resolve => {
setTimeout(t => resolve(x + 1), 10);
});
}
const p1 = sum1(1);
const p2 = p1.then(sum1).then(sum1);
Promise.all([p1]).then(v => console.log('P1', v[0]));
Promise.all([p2]).then(v => console.log('P2', v[0]));
Actually all I had to do was call the chain in the variable declaration, so it references the last called promise
function sum1(x){
return new Promise(resolve => {
setTimeout(t => resolve(x+1),3000)
})
}
//The change is here
const p1 = sum1(1)
.then(sum1)
.then(sum1)
Promise.all([p1])
.then(v => console.log(v[0]));
Explanation: the problem with your code was that you store in const p1 = sum1(1); only first part of chain, and in Promise.all([p1]) you get result only from this first part (one solution is just to store all chain in p1 like this: p1=sum1(1).then(sum1).then(sum1). However in your case, you don't need to use Promie.all at all (because in your example there is only one promise p1/2 ):
function sum1(x){
return new Promise(resolve => {
setTimeout(t => resolve(x+1),300)
})
}
// Promise.all([sum1(1).then(sum1).then(sum1)]).then(r => console.log(r)); // this works too
sum1(1).then(sum1).then(sum1).then(r => console.log(r));
How about create a new function to do your task, it seems like promise.all does not fit to your case
const runInWaterfall = (promises) => new Promise((resolve, reject) => {
const result = promises.reduce((acc, curr, index) => {
if(!acc) {
return curr();
}
return acc.then(curr);
}, null);
result.then(resolve).catch(reject);
})
and your task can be rewritten as following
runInWaterfall([() => Promise.resolve(1), sum1, sum1, sum1]).then(result => console.log(result))

Catching promise errors from a loop

I need to call multiple promises inside a for loop, but it gives me unhandled promise exception during each run. The way to solve this is to return the second promise, this way the last catch would be executed and it would work without errors, but - second and more iterations would not be executed.
const doFirstThing = () => {
return new Promise((resolve, reject) => {
setTimeout(() => (resolve(['a', 'b', 'c'])), 1000);
});
}
const doSecondThing = () => {
return new Promise((resolve, reject) => {
setTimeout(() => (reject('test')), 500); // reject here
});
}
doFirstThing()
.then(results => {
results.forEach(result => {
doSecondThing(result)
.then(() => console.log(result, 'done'));
});
})
.catch(error => console.log(error));
How can I deal with this?
To prevent unhandled promise exception chain .catch() to lat .then(); substitute using Promise.all() and .map() for .forEach()
const doFirstThing = () => {
return new Promise((resolve, reject) => {
setTimeout(() => (resolve(['a', 'b', 'c'])), 1000);
})
}
const doSecondThing = (r) => {
return new Promise((resolve, reject) => {
setTimeout(() => (reject('test')), 500); // reject here
})
}
doFirstThing()
.then(results => {
return Promise.all(results.map(result => {
return doSecondThing(result)
}));
})
.then((result) => console.log(result, 'done'))
.catch(error => console.log(`err:${error}`));

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