I try make some node script with co. It works well, but a have big delay before script finished. (I got "Ok" or "Bad" after one second, but script finishes 7 seconds after it). What I missed?
co(function *() {
let errors = yield someCheck(process.argv);
if (!errors) {
console.log('Ok');
} else {
console.log('Bad');
}
})(function(e) {
if (e) {
console.log(e);
}
});
I get a typeError when I run your code. I'm not sure what you are trying to do there, but I think you cannot pass the error handler as a second argument when calling co(), you have to use then() or catch() for error handling.
// Here's an example
co(function*() {
var result = yield Promise.resolve(true);
return result;
}).then(function(value) {
console.log(value);
}, function(err) {
console.error(err.stack);
});
// you can also catch the error
co(function *(){
// yield any promise
var result = yield Promise.resolve(true);
}).catch(onerror);
function onerror(err) {
// log any uncaught errors
// co will not throw any errors you do not handle!!!
// HANDLE ALL YOUR ERRORS!!!
console.error(err.stack);
}
I think process.exit() would fix your issue.
Related
I have typescript class with some functions in it. Each function has a try catch block where upon hitting the catch it returns a pre defined response.
I am writing unit tests using mocha and chai and I am having trouble trying to explicitly hit the catch blocks.
For example, consider this simple function below
public async verifyCode(email: string, code: string) {
try {
let result: CodeInterface | null = //call db to get matching code
if(result === null) {
return {status: 401, message: INCORRECT_FELLOWSHIP_CODE_MESSAGE};
}
return result._id;
} catch (error) {
Logger.log(LoggerLogTypes.ERROR, {
class_name: "LaunchServiceImpl",
function_name: "verifyFellowshipCode",
message: error.message,
stack: error.stack,
additional_info: {
code
}
});
return false;
}
}
I want to write a test case where I can just send the control directly to the catch block to get the false value. This is a very simplistic example but in few other functions I am doing a lot more in the catch block.
My mocha unit test looks like this:
it("should go to the catch block and return false in case of exception", async function() {
let serviceInstance = new MyClass();
let response = await serviceInstance.verifyCode("john#test.com", "abc123");
// how do I directly jump to the catch block here??
expect(response).to.equal(false);
});
Suppose you have a function that will throw an error with the message User not found so you can test like this:
profileServiceInstance.getProfile('1').catch((err) => {
expect(() => {
throw err;
}).to.throw(Error, 'User not found');
});
class AuthController {
static methods = {
GET: {
'/auth/signup': {
func: AuthService.signUp,
response: (data, res) => {
res.statusCode = 200;
res.end(JSON.stringify(data));
},
},
},
};
static use(req, res) {
const route = this.methods[req.method][req.url];
if (!route) {
res.statusCode = 404;
res.end(JSON.stringify({ message: 'Not found 404!' }));
return;
}
try {
const data = JSON.parse(req?.body?.data || '{}');
const result = route.func({ ...data });
route.response(result, res);
} catch (err) {
console.log(err, 'here');
res.statusCode = err.statusCode || 500;
res.end(JSON.stringify(err.message));
}
}
}
class AuthService {
static async signUp({ login, password }) {
if (!login || !password) throw new BaseError(400, 'kl', 'Custom error');
}
}
It shows the error in console but try catch block doesn't see it.
Here is the traceback.
I don't know what the reason is because the function which throws error is inside of the block. Help please!
The trace back that you attached tells you exactly what the problem is and what you need to do:
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()
You can't catch an exception thrown by an async function with a try..catch block outside of that function, because script execution reaches the catch block before the async execution is finished. You therefor have to use .catch(..) instead:
const result = route.func({ ...data }).catch((err) => {
console.log("catched error: ", err);
});
I see one issue. You have declared signUp() to be async. That means it always returns a promise and it means that any throw operations inside it reject that promise that it returns (the exception doesn't propagate synchronously). But, when you attempt to call it here:
const result = route.func({ ...data });
You don't await it so when signUp() rejects, the promise goes into result, but nobody ever handles the fact that the promise rejected and you get UnhandlePromiseRejectionWarning from the system.
I can't see the whole overall design (of all the other routes), but perhaps you just need to add await to this:
const result = await route.func({ ...data });
And, you would have to make .use() be async also.
Or, if signUp() doesn't actually need to be async, then just remove the async from its declaration and the throw will be synchronous (instead of being turned into a rejected promise) and your try/catch will catch it then.
I'm writing some code to do polling for a resource every N ms which should timeout after M seconds. I want the whole thing to be promise based using Bluebird as much as possible. The solution I've come up with so far uses node's interval, cancellable bluebird promises and bluebird's timeout function.
I'm wondering if there's a better way to do timing out intervals with bluebird and promises in general? Mostly by making sure the interval stops at the point and never continues indefinitely.
var Promise = require('bluebird');
function poll() {
var interval;
return new Promise(function(resolve, reject) {
// This interval never resolves. Actual implementation could resolve.
interval = setInterval(function() {
console.log('Polling...')
}, 1000).unref();
})
.cancellable()
.catch(function(e) {
console.log('poll error:', e.name);
clearInterval(interval);
// Bubble up error
throw e;
});
}
function pollOrTimeout() {
return poll()
.then(function() {
return Promise.resolve('finished');
})
.timeout(5000)
.catch(Promise.TimeoutError, function(e) {
return Promise.resolve('timed out');
})
.catch(function(e) {
console.log('Got some other error');
throw e;
});
}
return pollOrTimeout()
.then(function(result) {
console.log('Result:', result);
});
Output:
Polling...
Polling...
Polling...
Polling...
poll error: TimeoutError
Result: timed out
I would do something like this -
function poll() {
return Promise.resolve().then(function() {
console.log('Polling...');
if (conditionA) {
return Promise.resolve();
} else if (conditionB) {
return Promise.reject("poll error");
} else {
return Promise.delay(1000).then(poll);
}
})
.cancellable()
}
Also be aware of Promise constructor anti-pattern
Rene Wooller makes a really good point:
Warning: unfortunately, recursion in javascript like this will eventually saturate the call stack and result in out of memory exceptions
Even if it doesn't exception, this is wasted space, and the risk of an exception might encourage an overlong polling delay.
I think this is important enough to prefer setInterval:
var myPromise = new Promise((resolve, reject) => {
var id = window.setInterval(() => {
try {
if (conditionA) {
window.clearInterval(id);
resolve("conditionA");
} else if (conditionB) {
throw new Error("conditionB!");
}
} catch(e) {
window.clearInterval(id);
reject(e);
}
}, 1000);
});
There are a few npm packages that address this requirement, of which I like promise-waitfor the best. It's 38 lines long and does the job.
var myPromise = waitFor(() => {
if(conditionA) return true;
if(conditionB) throw new Error("conditionB!");
return false;
});
I've got a method that may throw an Error, but I'm having trouble writing a SinonJS/Mocha/Should unit test case for this condition.
Sample function under test:
function testError(value) {
if (!value) {
throw new Error('No value');
return false;
}
};
Sample test:
describe('#testError', function() {
it('throws an error', function() {
var spy = sinon.spy(testError);
testError(false);
spy.threw().should.be.true();
});
});
This outputs:
#testError
1) throws an error
0 passing (11ms)
1 failing
1) #testError throws an error:
Error: No value
at testError (tests/unit/js/test-error.js:6:14)
at Context.<anonymous> (tests/unit/js/test-error.js:14:6)
I was expecting Sinon to catch the Error and allow me to spy on the throw, but it seems to fail the test instead. Any ideas?
I referred to Don't sinon.js spys catch errors? but the only solution there is to use expect. I'd prefer to keep with a single assertion library if possible.
It appears that this works inside a try/catch:
function foo() { throw new Error("hey!"); }
var fooSpy = sinon.spy(foo);
try {
fooSpy();
} catch (e) {
// pass
}
assert(fooSpy.threw());
Note that you have to call fooSpy, not foo itself.
But also note that .should.be.true() is not part of Sinon, so you're probably already using Chai or a similar library, in which case the expect(foo).to.have.thrown() or assert.throws(foo, someError) syntax seems much nicer.
Update: If you're using ShouldJS, looks like you can use should.throws. I still think this is nicer than using the Sinon version for this purpose.
Revised
Following #nrabinowitz's helpful advice, here's a solution that uses should.throws. This avoids using Sinon.spy altogether.
describe('#testError', function() {
it('throws an error', function() {
should.throws(function() {
testError(false);
});
});
});
const bootstrap = async anyParam => {
if(!anyParam){
throw new Error('test')
}
await anyParam()
}
const spy = sinon.spy(bootstrap)
try {
await spy();
} catch (e) {
expect(e.message).to.be.equal('test')
spy.called = false
}
expect(spy.called).to.be.equal(false);
If I yield promises in Koa, they may be rejected:
function fetch = (){
var deferred = q.defer();
//Some async action which calls deferred.reject();
return deferred.promise;
}
this.body = yield fetch(); //bad, not going to work
Is there a error-handling pattern in Koa to handle this besides explicitly unwrapping the promise with then and handling the error explicitly?
Try/Catch. Under the hood koajs uses co. Check out the docs for co, which describes error handling a little better.
function fetch = (){
var deferred = q.defer();
//Some async action which calls deferred.reject();
return deferred.promise;
}
try{
this.body = yield fetch(); //bad, not going to work
}
catch(err){
this.throw('something exploded');
}
Here is an rudimentary example of what's happening with the promise:
function * someGenerator () {
let result;
try{
result = yield fetch();
console.log('won\'t make it here...');
}
catch(err){
console.log(err);
}
console.log('will make it here...');
}
// Normally co() does everything below this line, I'm including it for
// better understanding
// instantiate the generator
let gen = someGenerator();
// calling gen.next() starts execution of code up until the first yield
// or the function returns.
let result = gen.next();
// the value returned by next() is an object with 2 attributes
// value is what is returned by the yielded item, a promise in your example
// and done which is a boolean that indicates if the generator was run
// to completion
// ie result = {value: promise, done: false}
// now we can just call the then function on the returned promise
result.value.then(function(result){
// calling .next(result) restarts the generator, returning the result
// to the left of the yield keyword
gen.next(result);
}, function(err){
// however if there happened to be an error, calling gen.throw(err)
// restarts the generator and throws an error that can be caught by
// a try / catch block.
// This isn't really the intention of generators, just a clever hack
// that allows you to code in a synchronous style (yet still async code)
gen.throw(err);
});
If your still uncertain, here are a couple other things that might help:
Watch my screencast on JavaScript Generators here: http://knowthen.com/episode-2-understanding-javascript-generators/
and/or try the following code:
// test.js
'use strict';
let co = require('co'),
Promise = require('bluebird');
function fetch () {
let deffered = Promise.defer();
deffered.reject(new Error('Some Error'));
return deffered.promise;
}
co.wrap(function *(){
let result;
try{
result = yield fetch();
console.log('won\'t make it here...');
}
catch(err){
console.log(err);
}
console.log('will make it here...');
})();
then on the console run
$ node test.js
[Error: Some Error]
will make it here...
You can easily drop in the package koa-better-error-handler with npm install --save koa-better-error-handler and then implement it as such:
const errorHandler = require('koa-better-error-handler');
// override koa's undocumented error handler
app.context.onerror = errorHandler;
More info: https://github.com/niftylettuce/koa-better-error-handler