error thrown in callback function is caught by `flush`'s promise `.catch()`
yo1dog opened this issue · comments
If a callback function in the messages queue or the callback function passed to flush
throws an error, it will trigger the .catch()
handler in the axios promise chain and run the thrown error through the error handling code meant for axios errors. This also results in the callback being called twice. For example:
let timesCalled = 0;
analytics.track({...}, val => {
console.log('Call', ++timesCalled);
throw new Error('oops');
});
Call 1
Call 2
(node:14657) UnhandledPromiseRejectionWarning: Error: oops
at REPL1:1:124
at /home/mike/work/ladder-services/node_modules/analytics-node/index.js:264:37
at Array.forEach (<anonymous>)
at done (/home/mike/work/ladder-services/node_modules/analytics-node/index.js:264:17)
at /home/mike/work/ladder-services/node_modules/analytics-node/index.js:297:9
at processTicksAndRejections (internal/process/task_queues.js:95:5)
- axios request finishes and the promise is resolved.
.then()
is called (line 289).done()
is called.- Callback is called.
- "Call 1" printed.
- Callback throws error.
- The
.then()
promise is rejected. .catch()
is called (line 290).done()
is called again.- Callback is called again.
- "Call 2" printed.
- Callback throws error again.
- The
.catch()
promise is rejected. - Nothing handles the rejected promise so
UnhandledPromiseRejection
.
This is equivalent to:
try {
await axios.post(...);
done();
} catch(err) {
done(err);
}
To fix, you should break onto a new callstack using setImmediate
before calling the callbacks:
const done = err => {
setImmediate(() => {
callbacks.forEach(callback => callback(err))
callback(err, data)
})
}