I am using Oboe.js to parse a really really large JSON file
const promises = [];
oboe('http://domain/my-file.js')
.node('items.*', item => {
// parseItem() returns a rejected Promise because of invalid JSON items
promises.push(parseItem(item));
})
.done(() => {
Promise.all(promises).then(() => {
doSomething();
});
})
But my Browser console gets flooded with Uncaught (in promise)
. The same occurs if you write a promise in a setTimeout()
like
const promises = [];
setTimeout(() => {
promises.push(Promise.reject());
}, 500);
// some time in the future
Promise.all(promises);
What's really strange: modern browsers behave differently. In Firefox Developer Edition everything works without the error messages and in Chrome I get flooded with Uncaught (in promise)
. In Chrome you get the message instantly if you write Promise.reject();
without a catch. In Firefox and Safari nothing happens.
So what's the solution for this? Ignoring the message? I mean if this behavior is really in the official promise spec then promises in asynchronous code does not make really sense for me.
Your Oboe issue is based on the fact Oboe streams JSON in, so we never know in advance how many promises there are so we can't attach the responsibility to Promise.all
in advance. We can "promisify" Oboe to be able to return promises in its methods.
Typically, I'd use RxJS to automatically create a stream from the event emitter -and RxJS's methods can already return promises and then aggregate it. However - since I don't want a third party library here, and it has less teaching value - let's implement it ourselves:
function addMap(oboe) {
oboe.map = function(selector, mapper){
var promises = [];
return new Promise(function(resolve, reject){ // create a new promise
oboe.node(selector, function(match){
var result = mapper(match); // get result
// signal that we're handling the rejection to make sure it's not handled.
result.catch(function(){});
promises.push(result);
});
oboe.fail(reject);
oboe.done(function(){ resolve(promises); });
});
};
}
Which would let us do:
var o = oboe("foo");
addMap(o);
o.map("items.*", item => downloadItem(item)).then(result => {
// handle result here
});
Your setTimeout
issue is very contrived. The vast majority of people do not write code that looks like this in practice - in practice adding a error handler asynchronously is a pretty rare use case when not working against an API that forces you to do so (like the Oboe.js example).
What's really strange: modern browsers behave differently
This is because Firefox uses GC for detecting unhandled rejections and Chrome a timer. It's implementation detail - the only guarantee you'll have is that errors will not be logged if attached within a microtask (synchronously, or in a then
that executes on the same turn).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With