Node.js: Error when calling promise's callback function - node.js

I recently ran in a, for me, strange error. I have this simple function returning
a promise:
create(data) {
let _self = this;
return new Promise(function(resolve, reject) {
if(_self._validateData(data)) {
//is valid, so store in database...
} else {
reject(_self.error);
}
});
}
When the data is valid, the data is stored and there is no problem calling the resolve callback. However, when the data isn't valid, I immediately call the reject function. But when I do so I receive an error.
Is it possible that, at the time the process reaches the call of reject, the reject was not set by the calling function? Because when I run following code, it works perfectly:
create(data) {
let _self = this;
return new Promise(function(resolve, reject) {
if(_self._validateData(data)) {
//is valid, so store in database...
} else {
setTimeout(function(){reject(_self.error);}, 100);
}
});
}
I really want to avoid using this solution. Not only because it looks ugly to me, but also because I am not even sure, if I understand the error.
So any ideas what is wrong here and how can I solve this?
For everybody facing the same problem:
It is not really an error but a "feature" of VS Code Debugger (using v8).
See here and here for further information. The issue is called "synchron rejection". Also, I only misinterpreted it as an error. It is not. The debugger just pauses and points out, that there is an synchron rejection
I moved on using bluebird promises

Is it possible that, at the time the process reaches the call of reject, the reject was not set by the calling function?
No, that's not possible. You can reject the promise at any time.
Because when I [put it in a timeout], it works perfectly
I'll guess that you did not set up a rejection handler (.catch(…)) on your promise chain soon enough, and are receiving a "possibly unhandled promise rejection" warning because of that.

Related

Error: Transaction rejected with non-error: undefined

I am using knex npm version 0.15.2. while Rollback the transaction I'm getting the following error:
Error: Transaction rejected with non-error: undefined
Trx.rollback()
above function used for rollback.
Same code working for knex version 0.12.6
This is the function I used for commit/Rollback.
function Commit(pTrx, pIsCommit, pCallback) {
try {
var co = require("co");
var q = require('q');
var Q = q.defer();
co(function* () {
if (pIsCommit) {
yield pTrx.commit();
} else {
yield pTrx.rollback();
}
Q.resolve(pCallback('SUCCESS'));
}).catch(function (error) {
Q.reject(pCallback(error));
});
return Q.promise;
} catch (error) {
console.log(error)
}
}
This code could use some work. :) Here are a few things that pop out:
You don't need co or q anymore. Promises and async/await are built-in and much simpler to use. Async functions automatically return promises that will be resolved with the value returned or rejected if an error is thrown. Learn about async/await here: https://jsao.io/2017/07/how-to-get-use-and-close-a-db-connection-using-async-functions/
You shouldn't return success as a string. If the function completes without throwing an exception, then success is implied. I see people do this from time to time, but it's often for the wrong reasons.
You shouldn't accept callback in a function that returns a promise, it should be one or the other. The caller of your function will either pass a callback or await it's completion.
If you are going to use callbacks, then you should return null as the first parameter when successful. See the last sentence here: https://nodejs.org/en/knowledge/getting-started/control-flow/what-are-callbacks/
Your function name, Commit, starts with an uppercase. This convention is typically used to indicate that the function is a contstructor function and meant to be invoked with the new keyword.
Here's how the function could look once cleaned up:
async function commit(pTrx, pIsCommit) {
// Not using a try/catch. If an error is thrown the promise returned will be rejected.
if (pIsCommit) {
await pTrx.commit();
} else {
await pTrx.rollback();
}
// Not going to return anything. If we get to this point then success is implied when the promise is resolved.
}
A consumer of your function would call it with something like:
async function myWork() {
// do work; get pTrx
try {
await commit(pTrx, true);
// If I get here, then I can assume commit was successful, no need to check a return value of 'SUCCESS'
} catch (err) {
// handle error
}
}
It's hard to say where the issue is with the code in its current state. However, if the issue truly is with Knex, then you should probably post this as an issue in the Knex repo: https://github.com/tgriesser/knex/issues But you should write a reproducible test case that proves its an issue with Knex.

Bluebird promise failing to short circuit on reject

Maybe I just don't understand promises but I've used this pattern before and never had issues. Using bluebird within node.
I have this function which is getting hit:
function getStores() {
return new Bluebird((resolve, reject) => {
return Api.Util.findNearbyStores(Address,(stores) => {
if (!stores.result) {
console.log('one')
reject('no response');
console.log('two')
}
const status = stores.results.status
})
})
}
then hits both of my logs, continues past the if and throws
'one'
'two'
TypeError: Cannot read property 'Status' of undefined
Basically it keeps going right past the resolve.
My impression was the promise should immediately short circuit on reject and pass the rejection through as the resolution to the promise. Am I misunderstanding this?
Yes, you are misunderstanding this. reject(…) is not syntax (just like resolve(…) isn't either) and does not act like a return statement that exits the function. It's just a normal function call that returns undefined.
You should be using
if (!stores.result) reject(new Error('no response'));
else resolve(stores.results.status);
The "short-circuiting" behaviour of rejections is attributed to promise chains. When you have
getStores().then(…).then(…).catch(err => console.error(err));
then a rejection of the promise returned by getStores() will immediately reject all the promises in the chain and trigger the catch handler, ignoring the callbacks passed to then.

Writing async/await version of a callback

I'm trying to rewrite a callback as async/await but following code doesn't work and results in high CPU at the commented line.
const kurento = require('kurento-client')
function getKurentoClient (kmsUrl) {
return new Promise((resolve, reject) => {
kurento(kmsUrl, (error, kurentoClient) => {
if (error) {
return reject(error)
} else {
return resolve(kurentoClient) // never returns
}
})
})
}
async function test () {
let result = await getKurentoClient('ws://localhost:8888/kurento')
console.log('done')
}
test()
From the mozilla.org website:
The Promise.resolve(value) method returns a Promise object that is resolved with the given value. If the value is a thenable (i.e. has a "then" method), the returned promise will "follow" that thenable, adopting its eventual state; if the value was a promise, that object becomes the result of the call to Promise.resolve; otherwise the returned promise will be fulfilled with the value.
And from bluebird GitHub:
when promises resolve other promises or things with then(aka thenables, like _kurentoClient in this case) - they have to wait for it to call then itself.
As you have guessed, kurento clients have a then function (so is a thenable) so the Promise is trying to resolve it. Because of a bug (or not a bug. Honestly, I have not researched enough to determine that) it keeps resolving itself forever.
As far as I see, seems that this kurento commit tries to fix it, resolving to an "untheanble". I see that the commit is from 31 Oct 2016 but the latest release is from Sep 2016 so I think that the fixed version is not deployed.

Handling simple value rejects in a promise library

When the Bluebird module detects a non-Error reject, it reports Warning: a promise was rejected with a non-error.
So when we are writing a reusable promise library, how should we handle the situation when it is the client's code that throws simple values?
Simple my-library example:
var Promise = require('bluebird');
module.exports = function (cb) {
return new Promise(function (resolve, reject) {
try {
cb();
resolve('some data');
} catch (e) {
// should we do this?
reject(e);
// or should we do this?
// reject(e instanceof Error ? e : new Error(e));
}
});
};
A client that consumes it:
var lib = require('my-library');
lib(function () {
throw 'Regular text';
})
.catch(function (error) {
// - should we expect exactly 'Regular text' here?
// - or should it be wrapped into Error by the promise library?
});
In the example above we are going to see the same Warning: a promise was rejected with a non-error again, which in this case is obviously the client's fault, but the problem is - it will be reported against the promise library that did the reject.
This brings a question of how a reusable library should handle such cases.
Option 1: do nothing (just use reject(e)), let the client see the warning message Warning: a promise was rejected with a non-error.
Option 2: verify if the error thrown is an instance of Error and if not - do reject(new Error(e)). This enables the client to throw and reject with simple values, which is probably bad.
I am looking for a general recommendation here.
Also consider that sometimes the client can be an old library that you are trying to wrap into promises.
You should definitely and objectively wrap non-errors with errors.
Non errors are terrible for debugging, the bluebird warning is there because it is very hard to figure out where an error came from if it's a primitive. You can't attach properties to primitives and they have no stack traces.
Stack traces mean you don't have to guess where the errors come from - you know.
That said, bluebird already does this automatically with promisify and promisifyAll and consider using them. If you're doing this manually - just make sure you throw or reject things with stacks too.
Node in addition looks for the stack traces of your unhandled rejections - so rejecting with junk will not only give you useless information in Node but it might actually crash your program.
Error messages should be wrapped in error objects, but not automatically.
Automatic wrapping reduces the usefulness of stack traces. For example, lets say the client function is f and your library function is g:
function f(x) { throw 'Hello world' }
function g(f, x) { try { f(x); } catch (e) { throw new Error(e); } }
g(f, 1)
The client function and line numbers where the error originated will be completely missing from the stack:
Error: Hello world
at g (repl:1:52)
at repl:1:1
While if the client function creates the error object:
function f(x) { throw new Error('Hello world'); }
function g(f, x) { try{ f(x); } catch (e) { throw e; } }
g(f, 1)
we get a full stack trace:
Error: Hello world
at f (repl:1:23)
at g (repl:1:25)
at repl:1:1
There is no way to recover the stack up to the point in your library, making it useless for users (they wont be able to debug their own code).
That said, this largely depends on what sort of library you're making and what sort of contract you would like it to present to the users. You should think very carefully before using user-passed callbacks, as they invert the normal chain of responsibility in the stack.
Promises make this easier because they un-invert the inversion: the library is called first to generate a promise, then that promise is passed a callback. As such, creating a new error together with the creation of the promise allows Bluebird to at least partially look into / augment the stack of the client code (the line that created the promise will be present in the stack). The warning is still there because its impossible to provide a complete stack.
No, they simply want you to wrap your JSON error objects into something.
This is not freedom, it's dictature.
There's no point in wrapping simple JSON objects in bulky Error wrappers.
Don't listen to them.

bluebird pattern for mocking asynchronous promises with syncronous code

I'm just getting started with bluebird (and node more generally).
I am wanting to create some mocks (for what will ultimately be either http or database calls).
All the talk of antipatterns has me anxious :-)
So is this a reasonable approach for converting this synchronous code into promises?
I have two simple functions here. The second calls the first (not in way the prod service would) but it serves as an illustration of nesting promises.
// mock function to return a promise of a collection
var getTestPatients = function(params) {
return new Promise(function(resolve, reject) {
setTimeout(function() {resolve(test_patients);}, 200);
})
};
// mock function to return a promise of an object
var getTestPatient = function(params) {
return getTestPatients().then(function(patients) {
// wouldnt get entire patient list in prod - but this works for a mock
var patient = _.find(patients, {urn: params.urn});
if (patient) {
patient.reviews = testReviews(params.uid);
}
else {
throw new Error('patient not found');
}
return Promise.resolve(patient);
});
};
A couple of things these are demonstrating:
creating a fake delay
creating a promise from synchronous code
manipulating a promise result within a function that makes a promise
throwing errors
So quite a few things I could have gotten wrong.
How did I do? Any anti-patterns in here? or other rookie errors?
No antipatterns and no mistakes to see here. However, two things you could improve:
Whenever you find yourself using the Promise constructor, first try to find a simpler way. Is there already a function that does return a promise for the desired task? setTimeout is a genuine callback-based API, so nothing wrong here, but given that you're using Bluebird you could remove that boilderplate by just using the Promise.delay helper function:
function getTestPatients = function(params) {
return Promise.delay(test_patients, 200);
}
The call to Promise.resolve in the then handler is unnecessary. Just as you can throw exceptions, you can return plain values and they will be automatically wrapped. So cleaner-looking is just
return patient;

Resources