I'm writing a function, that loops through port numbers until it finds an open one:
async function findPort (port, app, logger) {
const lookupPort = () => {
return portOpen = new Promise( (resolve, reject) => {
app.listen(port, () => {
logger.log("info", `Server listening on port: ${port}`);
resolve(true);
})
.on('error', (err) => {
logger.log("warn", "port closed: " + err.port);
reject(false);
});
});
}
let portOpen = false;
while (portOpen === false){
portOpen = await lookupPort();
port++;
}
When executed, it iterates through one port and then trows an exception:
(node:2869) UnhandledPromiseRejectionWarning: Unhandled promise rejection.
There are 2 issues with the code above:
1) while loop stops its execution because lookupPort function throws an error (promise is rejected on reject(false); line) which is not caught (UnhandledPromiseRejectionWarning: Unhandled promise rejection.). To fix this try to wrap the code inside while loop into try/catch construction:
while (portOpen === false){
try {
portOpen = await lookupPort();
}
catch (error) {
// handle an error here
}
port++;
}
Or if you do not want to handle the promise you reject inside lookupPort just remove reject(false); line.
2) lookupPort function assigns a new Promise() to portOpen variable. So even if error will be handled by try/catch, portOpen === false condition will evaluate to false because portOpen will be equal to a Promise object. If there is no reason to assin portOpen the value of the new Promise you should remove it.
Related
I'm having trouble to properly catch an error/reject in a promise chain.
const p1 = () => {
return new Promise((resolve, reject) => {
console.log("P1");
resolve();
});
};
const p2 = () => {
return new Promise((resolve, reject) => {
console.log("P2");
reject();
});
};
const p3 = () => {
return new Promise((resolve, reject) => {
console.log("P3");
resolve();
});
};
p1().catch(() => {
console.log("Caught p1");
}).then(p2).catch(() => {
console.log("Caught p2");
}).then(p3).catch(() => {
console.log("Caught p3");
}).then(() => {
console.log("Final then");
});
When the promise is rejected, the following .then still gets executed. In my understanding, when in a promise chain an error/reject happened, the .then calls that follow it are not executed any more.
P1
P2
Caught p2
P3
Final then
The rejection gets caught correctly, but why is "P3" logged after the catch?
What am I doing wrong?
To clarify #evolutionxbox, this is my expected result:
Promise.resolve().then(() => {
console.log("resolve #1");
return Promise.reject();
}).then(() => {
console.log("resolve #2");
return Promise.resolve();
}).then(() => {
console.log("resolve #3");
return Promise.resolve();
}).then(() => {
console.log("Final end");
}).catch(() => {
console.log("Caught");
});
This code works exactly like it should. And I can't see a difference to my code, except that I declared the functions separately.
The code above stops no matter where the promise is rejected.
Here is a synchronous equivalent of your code:
const f1 = () => {
console.log("F1");
};
const f2 = () => {
console.log("F2");
throw new Error();
};
const f3 = () => {
console.log("F3");
};
try {
f1();
} catch {
console.log("Caught f1");
}
try {
f2();
} catch {
console.log("Caught f2");
}
try {
f3();
} catch {
console.log("Caught f3");
}
console.log("Final code");
As you can see, that gives a matching result. Hopefully, looking at the synchronous code you would not be surprised why. In a try..catch you are allowed to attempt recovery. The idea is that the catch will stop the error propagation and you can hopefully continue further. Or if you do want to stop, you still have to explicitly throw again, for example:
doCode();
try {
makeCoffee();
} catch(err) {
if (err instanceof IAmATeapotError) {
//attempt recovery
makeTea();
} else {
//unrecoverable - log and re-throw
console.error("Fatal coffee related issue encountered", err);
throw err;
}
}
doCode();
This is also the purpose Promise#catch() serves - so you can attempt recovery or at least act when there was a problem. The idea is that after the .catch() you might be able to continue:
const orderPizza = (topping) =>
new Promise((resolve, reject) => {
if (topping === "pepperoni")
reject(new Error("No pepperoni available"));
else
resolve(`${topping} pizza`);
});
const makeToast = () => "toast";
const eat = food => console.log(`eating some ${food}`);
async function main() {
await orderPizza("cheese")
.catch(makeToast)
.then(eat);
console.log("-----");
await orderPizza("pepperoni")
.catch(makeToast)
.then(eat);
}
main();
In order to reject the promise chain from a .catch() you need to do something similar as a normal catch and fail at the error recovery by inducing another error. You can throw or return a rejected promise to that effect.
This code works exactly like it should. And I can't see a difference to my code, except that I declared the functions separately.
The code above stops no matter where the promise is rejected.
The second piece of code you show fails entirely after a reject because there are no other .catch()-es that are successful. It is basically similar to this synchronous code:
try {
console.log("log #1");
throw new Error();
console.log("log #2");
console.log("log #3");
console.log("Final end");
} catch {
console.log("Caught");
}
Thus if you do not want to recover early, you can also skip the .catch() instead of inducing another error.
Try this.
const p1 = (arg) => {
// Promise returns data in the respected arguments
return new Promise((resolve, reject) => {
// Data to be accessed through first argument.
resolve(arg);
});
};
const p2 = (arg) => {
return new Promise((resolve, reject) => {
// Data to be accessed through second argument.
reject(arg);
});
}
p1('p1').then(resolve => {
console.log(resolve + ' is handled with the resolve argument. So it is accessed with .then()');
}) // Since reject isn't configured to pass any data we don't use .catch()
p2('p2').catch(reject => {
console.log(reject + ' is handled with the reject argument. So it is accessed with .catch()');
}) // Since resolve ins't configured to pass any data we don't use .then()
// You would normally configure a Promise to return a value on with resolve, and access it with .then() when it completes a task successfully.
// .catch() would then be chained on to the end of .then() to handle errors when a task cannot be completed.
// Here is an example.
const p3 = () => {
return new Promise((resolve, reject) => {
var condition = true;
if (condition === true) {
resolve('P3');
} else {
reject('Promise failed!');
}
});
};
p3('p3').then(resolve => {
console.log(resolve);
}).catch(reject => {
console.log(reject);
})
You do not do anything wrong.
In your code you call the first promise p1. Then you write p1.catch(...).then(...).then(...).then(...). This is a chain which means that you should call then 3 times, because you called resolve method in the p1 promise (all these thens depend on the first promise).
When the promise is rejected, the following .then still gets executed.
Yes. Just to be accurate: the then and catch method calls are all executed synchronously (in one go), and so all promises involved are created in one go. It's the callbacks passed to these methods that execute asynchronously, as the relevant promises resolve (fullfill or reject).
In my understanding, when in a promise chain an error/reject happened, the .then calls that follow it are not executed any more.
This is not the case. The promise that a catch returns can either fullfill or reject depending on what happens in the callback passed to it, and so the callbacks further down the chain will execute accordingly when that promise resolves.
The rejection gets caught correctly, but why is "P3" logged after the catch?
As in your case the catch callback returns undefined (it only performs a console.log), its promise fullfulls! By consequence, the chained then callback -- on that promise -- is executed... etc.
If you want to "stop"
If you want to keep the chain as it is, but wish to have a behaviour where a rejection leads to no further execution of then or catch callbacks, then don't resolve the associated promise:
const stop = new Promise(resolve => null);
const p1 = () => {
return new Promise((resolve, reject) => {
console.log("P1");
resolve();
});
};
const p2 = () => {
return new Promise((resolve, reject) => {
console.log("P2");
reject();
});
};
const p3 = () => {
return new Promise((resolve, reject) => {
console.log("P3");
resolve();
});
};
p1().catch(() => {
console.log("Caught p1");
return stop; // don't resolve
}).then(p2).catch(() => {
console.log("Caught p2");
return stop;
}).then(p3).catch(() => {
console.log("Caught p3");
return stop;
}).then(() => {
console.log("Final then");
});
I have a function which wraps a third party child-process-promise, which itself wraps spawn in promise.
let spawn = require('child-process-promise').spawn;
run(cmd, args = []) {
return new Promise(async (resolve, reject) => {
let command = spawn(cmd, args);
let childProcess = command.childProcess;
let result = '';
childProcess.stdout.on('data', (data) => {
result += data.toString();
});
try {
const res = await command;
resolve(result);
} catch (err) {
if (err.code && err.code === 'ENOENT') {
reject(`Command "${cmd}" not found`);
} else {
reject('Exec err' + err);
}
}
});
}
Testing the resolve was quite straightforward and I manage to get my stdout data passed to result then detected by chai-as-promised using await expect(shellRun).to.eventually.become('hello world');
Our problem is when we try to test the catch part of our method.
const ERROR = 'someError';
beforeEach(() => {
sandbox = sinon.createSandbox();
spawnEvent = new events.EventEmitter();
spawnEvent.stdout = new events.EventEmitter();
spawnStub = sandbox.stub();
spawnStub.returns({ childProcess: spawnEvent });
spawnStub.withArgs(ERRORED, ARGUMENTS).throws(ERROR));
shell = proxyquireStrict('../../lib/utils/spawnWrapper', {
'child-process-promise': {
spawn: spawnStub
}
}
);
});
afterEach(() => {
sandbox.restore();
});
describe('when a generic error occurs', () => {
it('should reject the promise', async () => {
const shellRun = run(ERRORED, ARGUMENTS);
await expect(shellRun).to.eventually.be.rejectedWith('Exec err' + ERROR);
});
});
We manage to get childProcessPromiseSpawn to throw an error conditionally by playing with ou spawnStub.withArgs. But a timeout is encountered:
(node:15425) UnhandledPromiseRejectionWarning: Error: someError
(node:15425) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 838)
1 failing
1) run method
when a generic error occurs
should reject the promise:
Error: Timeout of 2000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves.
We tried spawnStub.withArgs(ERRORED, ARGUMENTS).rejects instead of throws without more success. Changing someError for new Error('someError') doesn't work either.
We also tried to catch at test level
try {
await run(ERRORED, ARGUMENTS);
} catch (e) {
expect(e).to.equal('Exec err' + ERROR);
}
But the timeout still occurs.
This depend of which testing library you are using. Each library has a dedicated timeout.
For mocha you can define it in the test suite or for a unique test
https://mochajs.org/#timeouts
I have a service that analyses websites, compresses their sources like CSS Documents, Images etc. I have 2 functions, one is Socket.IO socket.on() method with async callback function. Another is main function for service.
socket.on('run', async options => {
debug(`${options.target} Adresine Bir Kullanıcı İstek Yaptı!`);
let user = null;
console.log(options);
if(options.token) {
user = await User.findById(jwt.verify(options.token, config.get('jwtPrivateKey'))._id);
options.userId = user._id.toString();
} else if(options.visitor) {
user = await Visitor.findById(options.visitor._id);
if(user.report) {
return socket.emit('error', new Error('You have exceeded your report limit'));
} else {
options.userId = user._id.toString();
}
}
if(options.userId) {
let userType = await UserType.find({ name: user.type });
if(userType.length > 0 && ((user.type == 'Visitor' && user.report == undefined) || (user.reports.length < userType[0].rights.reportsLimit.limit || userType[0].rights.reportsLimit.unlimited))) {
options.rights = userType[0].rights;
let { error, data } = await wrapper(runService(options.target, options, socket));
if(error) {
console.log('Here', error);
return socket.emit('error', error);
}
.
.
.
}
.
.
.
}
});
In the above function,
let { error, data } = await wrapper(runService(options.target, options, socket));
if(error) {
console.log('Here', error);
return socket.emit('error', error);
}
This part is important, because I call my main async service function runService with my async function wrapper function that is named wrapper. The wrapper function is this;
const wrapper = promise => (
promise
.then(data => ({ data, error: null }))
.catch(error => ({ error, data: null }))
);
In my main async service function, I only throw an error;
async function runService(target, options, socket) {
throw new Error('any error');
}
But the expected output is much different from actual output. Here is the output of this code;
Here Error: any error
at startService (C:\Projeler\OpDetect\Background-Service\lib\app.js:404:11)
at Socket.socket.on (C:\Projeler\OpDetect\Background-Service\app.js:73:57)
at process._tickCallback (internal/process/next_tick.js:68:7)
(node:16600) UnhandledPromiseRejectionWarning: Error: any error
at startService (C:\Projeler\OpDetect\Background-Service\lib\app.js:404:11)
at Socket.socket.on (C:\Projeler\OpDetect\Background-Service\app.js:73:57)
at process._tickCallback (internal/process/next_tick.js:68:7)
(node:16600) UnhandledPromiseRejectionWarning: Unhandled promise rejection.
This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 2)
(node:16600) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate
the Node.js process with a non-zero exit code.
My expectation about the output is, like this;
Here Error: any error
at startService (C:\Projeler\OpDetect\Background-Service\lib\app.js:404:11)
at Socket.socket.on (C:\Projeler\OpDetect\Background-Service\app.js:73:57)
at process._tickCallback (internal/process/next_tick.js:68:7)
Because I already handled the promise rejection with my wrapper function and catched the rejection, Why is 2 more UnhandledPromiseRejectionWarning errors on rejection?
Also, the line,
return socket.emit('error', error);
is not calling for no reason. It should have been called when the if statement truthy. Why is not this socket.emit function called?
As best practice use try {} catch(){} with async/await.
For ex.
userUtils.signUp = async (userName) => {
try {
const callFunction = await userUtils.checkExistancy(userName);
if (!callFunction.isExist) {
...
} else {
...
}
} catch (err) {
console.log(err);
throw err;
}
};
in your case it will be like
socket.on('run', async options => {
try {
user = await User.findById(jwt.verify(options.token, config.get('jwtPrivateKey'))._id);
options.userId = user._id.toString();
return true;
} catch (err) {
throw err;
}});
UnhandledPromiseRejectionWarning on async await promise
I have this code:
function foo() {
return new Promise((resolve, reject) => {
db.foo.findOne({}, (err, docs) => {
if (err || !docs) return reject();
return resolve();
});
});
}
async function foobar() {
await foo() ? console.log("Have foo") : console.log("Not have foo");
}
foobar();
Which results with:
(node:14843) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): false
(node:14843) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
Note: I know I can solve this issue like this:
foo().then(() => {}).catch(() => {});
But then we are "back" to callbacks async style.
How do we solve this issue?
Wrap your code in try-catch block.
async function foobar() {
try {
await foo() ? console.log("Have foo") : console.log("Not have foo");
}
catch(e) {
console.log('Catch an error: ', e)
}
}
then(() => {}).catch(() => {}) isn't needed because catch doesn't necessarily should go after then.
UnhandledPromiseRejectionWarning means that a promise weren't synchronously chained with catch, this resulted in unhandled rejection.
In async..await, errors should be caught with try..catch:
async function foobar() {
try {
await foo() ? console.log("Have foo") : console.log("Not have foo");
} catch (error) {
console.error(error);
}
}
The alternative is to handle errors at top level. If foobar is application entry point and isn't supposed to be chained anywhere else, it's:
foobar().catch(console.error);
The problem with foo is that it doesn't provide meaningful errors. It preferably should be:
if (err || !docs) return reject(err);
Also, most popular callback-based libraries have promise counterparts to avoid new Promise. It mongoist for mongojs.
Every solution here just silences the error, but you should probably handle the error instead.
How you handle it depends on the error and on what part of the application you're in. Here are some examples.
You're writing an app
If you're writing a node app and something throws, you might want to use process.exit(1) to quit the app and display the error to the user:
async function init() {
await doSomethingSerious();
}
init().catch(error => {
console.error(error);
process.exit(1)
});
You're writing a module
If the code expects an error, you can catch it and use it as a value instead:
module.exports = async function doesPageExist(page) {
try {
await fetchPage(page);
return true;
} catch (error) {
if (error.message === '404') {
return false;
}
// Unrecognized error, throw it again
throw error;
}
}
Notice that this example re-throws the error when it's not the expected one. This is fine. It's the final user’s responsibility to handle network errors:
const doesPageExist = require('my-wonderful-page-checker');
async function init() {
if (await doesPageExist('https://example.com/nope')) {
console.log('All good')
} else {
console.log('Page is missing 💔')
}
}
// Just like before
init().catch(error => {
console.error(error);
process.exit(1)
});
You're the user
If you're seeing this error when using a prepackaged application via command line, like webpack or babel, it might mean that the application had an error but it was not handled. This depends on the application or your input is not correct. Refer to the application’s manual.
What is the best way to handle this scenario. I am in a controlled environment and I don't want to crash.
var Promise = require('bluebird');
function getPromise(){
return new Promise(function(done, reject){
setTimeout(function(){
throw new Error("AJAJAJA");
}, 500);
});
}
var p = getPromise();
p.then(function(){
console.log("Yay");
}).error(function(e){
console.log("Rejected",e);
}).catch(Error, function(e){
console.log("Error",e);
}).catch(function(e){
console.log("Unknown", e);
});
When throwing from within the setTimeout we will always get:
$ node bluebird.js
c:\blp\rplus\bbcode\scratchboard\bluebird.js:6
throw new Error("AJAJAJA");
^
Error: AJAJAJA
at null._onTimeout (c:\blp\rplus\bbcode\scratchboard\bluebird.js:6:23)
at Timer.listOnTimeout [as ontimeout] (timers.js:110:15)
If the throw occurs before the setTimeout then bluebirds catch will pick it up:
var Promise = require('bluebird');
function getPromise(){
return new Promise(function(done, reject){
throw new Error("Oh no!");
setTimeout(function(){
console.log("hihihihi")
}, 500);
});
}
var p = getPromise();
p.then(function(){
console.log("Yay");
}).error(function(e){
console.log("Rejected",e);
}).catch(Error, function(e){
console.log("Error",e);
}).catch(function(e){
console.log("Unknown", e);
});
Results in:
$ node bluebird.js
Error [Error: Oh no!]
Which is great - but how would one handle a rogue async callback of this nature in node or the browser.
Promises are not domains, they will not catch exceptions from asynchronous callbacks. You just can't do that.
Promises do however catch exceptions that are thrown from within a then / catch / Promise constructor callback. So use
function getPromise(){
return new Promise(function(done, reject){
setTimeout(done, 500);
}).then(function() {
console.log("hihihihi");
throw new Error("Oh no!");
});
}
(or just Promise.delay) to get the desired behaviour. Never throw in custom (non-promise) async callbacks, always reject the surrounding promise. Use try-catch if it really needs to be.
After dealing with the same scenario and needs you are describing, i've discovered zone.js , an amazing javascript library , used in multiple frameworks (Angular is one of them), that allows us to handle those scenarios in a very elegant way.
A Zone is an execution context that persists across async tasks. You can think of it as thread-local storage for JavaScript VMs
Using your example code :
import 'zone.js'
function getPromise(){
return new Promise(function(done, reject){
setTimeout(function(){
throw new Error("AJAJAJA");
}, 500);
});
}
Zone.current
.fork({
name: 'your-zone-name',
onHandleError: function(parent, current, target, error) {
// handle the error
console.log(error.message) // --> 'AJAJAJA'
// and return false to prevent it to be re-thrown
return false
}
})
.runGuarded(async () => {
await getPromise()
})
Thank #Bergi. Now i know promise does not catch error in async callback. Here is my 3 examples i have tested.
Note: After call reject, function will continue running.
Example 1: reject, then throw error in promise constructor callback
Example 2: reject, then throw error in setTimeout async callback
Example 3: reject, then return in setTimeout async callback to avoid crashing
// Caught
// only error 1 is sent
// error 2 is reached but not send reject again
new Promise((resolve, reject) => {
reject("error 1"); // Send reject
console.log("Continue"); // Print
throw new Error("error 2"); // Nothing happen
})
.then(() => {})
.catch(err => {
console.log("Error", err);
});
// Uncaught
// error due to throw new Error() in setTimeout async callback
// solution: return after reject
new Promise((resolve, reject) => {
setTimeout(() => {
reject("error 1"); // Send reject
console.log("Continue"); // Print
throw new Error("error 2"); // Did run and cause Uncaught error
}, 0);
})
.then(data => {})
.catch(err => {
console.log("Error", err);
});
// Caught
// Only error 1 is sent
// error 2 cannot be reached but can cause potential uncaught error if err = null
new Promise((resolve, reject) => {
setTimeout(() => {
const err = "error 1";
if (err) {
reject(err); // Send reject
console.log("Continue"); // Did print
return;
}
throw new Error("error 2"); // Potential Uncaught error if err = null
}, 0);
})
.then(data => {})
.catch(err => {
console.log("Error", err);
});