I'm in the process of building a small Library that relies heavily on asynchronicity and I'm trying to support NodeJs callback style, Promises and async/await with minimal duplication of functional code.
So, as example take the following ( it doesn't matters what it does)
class Someclass{
constructor(){}
asyncMethod(someData, cb){
var commonVar = ''
if (cb) { // ******* Callback Requested
doSomething()
var result=usingCallbacks()
if (!result) cb('Error')
cb(null, result)
} else{ // ******** we should use a promise
return new Promise((resolve,reject) => {
doSomething()
var result=usingPromises()
if (!result) reject(err)
resolve(result)
})
}
}
}
So i'm stuck on how to build the async/awit part here. Any ideas?
Nothing else is required since await can await anything that returns a Promise (or something promise-like).
In other words, this should "just work":
async useLibFn() {
await someclass.asyncMethod('foo');
}
Under the covers, async/await are just syntactic sugar for promises.
async causes a function to always return a promise. Basically equivalent to:
function someFn() {
return new Promise(function(resolve, reject) {
try {
// actual fn body
} catch (ex) {
reject(ex);
}
});
}
await takes some expression and resolves it asynchronously. If the value is promisey, it waits for the promise to resolve (calls then()). Otherwise, it resolves with the value on the next tick. Basically equivalent to:
Promise.resolve(rval).then(...)
Related
I want to call a given function asynchronously. The wrapper function tryCallAsync is one way of doing this. This approach works. However, it requires that the callback for setImmediate to be an async function. This seems wrong, as the callback is returning a Promise that is not used. Is it wrong to pass an async function to setImmediate for this purpose?
async function tryCallAsync(fn, ...args) {
return new Promise((r, j) => {
setImmediate(async () => {
try {
r(await fn(...args));
}
catch (e) {
j(e);
}
})
})
}
// Using tryCallAsync
let resolveAsync = tryCallAsync(()=>{
return new Promise((r,j)=>{
setImmediate(()=>r('resolveAsync'));
});
})
resolveAsync.then((resolve)=>console.log(resolve));
let resolve = tryCallAsync(()=>{
return 'resolve';
});
resolve.then((resolve)=>console.log(resolve));
NB: https://www.youtube.com/watch?v=e3Nh350b6S4
Yes, it's wrong, for multiple reasons:
setImmediate doesn't handle the returned promise, especially it doesn't deal with errors1
Don't put business logic in asynchronous (non-promise) callbacks when using promises. Settle a promise from there, nothing else.
1: And even while your particular callback never rejects the returned promise due to the try/catch, it still feels wrong
Your function should be written as
async function tryCallAsync(fn, ...args) {
await new Promise(resolve => {
setImmediate(resolve);
});
return fn(...args);
}
This approach doesn't waste a Promise, however, still, it's not as performant as the conventional way of doing this.
function tryCallAsync(fn, ...args) {
return new Promise((r, j) => {
setImmediate(() => {
(async function () {
return await fn(...args);
})().then(r).catch(j);
});
});
}
How to handle multiple calls to the same function when its returning nothing. I need to wait untill all calls are finished so i can call another function.
For now I'm using Promise.all() but it doesn't seem right:
Promise.all(table_statements.map(i => insertValues(i)))
.then(function(result) {
readNodeData(session, nodes);
})
.catch(function() {
console.log(err);
})
function insertValues(statement) {
return new Promise((res, rej) => {
database.query(statement, function (err, result) {
if (err) {
rej(err)
}
else{
console.log("Daten in Tabelle geschrieben")
res(); // basically returning nothing
}
});
});
}
This writes data to a database in multiple statements, i need to wait untill all are finished.
Is this actually the "right" way to do it? I mean... it works, but i have the feeling it's not how you are supposed to do it.
Using Promise.all for your case is a good call, since it returns a Promise, when all the promises passed as an iterable are resolved. See the docs.
However, for brevity and readability, try converting your insertValues into async-await function as follows. This tutorial would be a great place to start learning about async functions in JavaScript.
// async insertValues function - for re-usability (and perhaps easy unit testing),
// I've passed the database as an argument to the function
async function insertValues(database, statement) {
try {
await database.query(statement);
} catch (error) {
console.error(error);
}
}
// using the insertValues() function
async function updateDatabase(database) {
try {
// I am using 'await' here to get the resolved value.
// I'm not sure this is the direction you want to take.
const results = await Promise.all(
tableStatements.map(statement => insertValues(database, statement))
);
// do stuff with 'results'.. I'm just going to log them to the console
console.log(results);
} catch (error) {
console.error(error);
}
}
Here, insertValues() function doesn't return any value. Its operation on the database is entirely dependent on the query statement passed to it. I wrapped it within a try-catch block so as to catch any errors that might arise while performing the operation (s) above. More details on handling errors using try-catch can be found here.
Your promisified write to database looks ok, so we can update code from another part.
Let's rewrite it a little to use async/await and try/catch.
(async() => {
const promisifiedStatements = table_statements.map(i => insertValues(i));
try {
await Promise.all(promisifiedStatements);
readNodeData(session, nodes);
} catch(e){
console.log(e)
}
})();
I use here IIFE to use await behaviour.
I need the callback function of find from Node.js mongodb 3.1.6 to be triggered before the return statement of an async function, however the return statement is called before the callback function even-though there is a wait in front.
async function(myId) {
const myObject = MyObject()
await collection.find({where: {id: myId}}, async (err, results) => {
if (err) {
logger.error('error');
}
myObject.add(results);
});
return myObject
}
I have seen some examples where instead of find(query, callback) the pattern find(query).toArray() was used. But this doesn't run at all in my case. We use Node.js mongodb 3.1.6 with loopback-connector-mongodb maybe this is related to the problem.
If mongo doesn't provide a promise-answering function, then promisify this one yourself. Neither that promise-creating wrapper nor the anonymous callback it uses should be declared async, but the caller should....
function findById(collection, myId) {
return new Promise((resolve, reject) => {
collection.find({where: {id: myId}}, (err, results) => {
(err)? reject(err): resolve(results);
});
});
}
// now callers can use the async await pattern...
async someFunction() {
try {
let myId = // ...
let collection = // ...
let results = await findById(collection, myId);
// do something with results
} catch (err) {
// error
}
}
The key idea is that collection.find with the callback isn't eligible for await, because it doesn't return a promise. The anonymous callback function you pass to it isn't an async function... it does its work right away, as soon as find calls it back. So we build a promise around mongo, then use the new async/await syntax with that promise.
So I'm new to Node.js and Im just wondering if the way I have my code setup makes sense. Im coming from a Java background so the nested callback structure is new. I have a Node program that runs a bunch of code that I broke down into different methods. The thing is that the methods need to be called in order. My code has this structure right now:
functionOne(data, callback(err) {
functionTwo(data, callback(err) {
functionThree(data, callback(err) {
functionFour(data, callback(err) {
//Code
});
});
});
});
This is very minimalistic, but is this structure ok? With Java, I'd take the return values of all the methods, then just pass them to the next function. From my understanding so far, the Java approach I just mentioned is one of the main things that Node.js was trying to eliminate. But anyway... Does that structure look ok, and is that how its intended to look? Just want to be sure that I'm not making any major errors with Node in general. Thanks!
Your code structure looks fine if you work with callback pattern.
But if you're interested in make your code cleaner and readable you would like to use Promises in your asynchronous function, so instead of pass a callback to your functions you could do something like this :
function asyncFunction (data){
return new Promise(function(resolve, reject){
// Do something with data
// Here you can call reject(error) to throw an error
resolve();
});
}
And instead of nested function callbacks you can call then method of Promise.
asyncFunction(data)
.then(function(){
// Promise resolved
// Something has been done with data
});
With Promises you can also execute async fuctions in parallel :
Promise.all([asyncFunctionA(data), asyncFunctionB(data), asyncFunctionC(data)])
.then(function(){...});
EDIT
If you need to pass values of one function to another, your code should look like this :
asyncFunctionA(data)
.then(function(dataA){
return asyncFunctionB(dataA);
})
.then(function(dataB){
return asyncFunctionC(dataB);
})
.then(function(dataC){
// ...
});
You should try to use promises to avoid your callback hell, so it could be something like these...
const Q = require('q'); // you can do a research for this module.
var myModule = {};
myModule.functionOne = (params) => {
const deferred = Q.defer(); // wait for this to complete
// body function
deferred.resolve(data); // this would be the result of this function
return deferred.promise; // data is the output on your function
}
myModule.functionTwo = (params) => {
const deferred = Q.defer(); // wait for this to complete
// body function
deferred.resolve(data); // this would be the result of this function
return deferred.promise; // data is the output on your function
}
myModule.doAll = (params) => {
myModule.functionOne(params)
.then((outputFunctionOne) => {
// this is called after functionOne ends
return myModule.functionTwo(outputFunctionOne);
})
.then((outputFunctionTwo) => {
// this is called after function 2 ends
if (outputFunctionTwo.success) {
// if everything ok, resolve the promise with the final output
deferred.resolve(outputFunctionTwo);
} else {
// reject the promise with an error message
deferred.reject('error');
}
})
.fail((err) => {
// this is call if the promise is rejected or an exception is thrown
console.log(err); // TODO: Error handling
})
.done();
}
module.exports = myModule;
You can Chain as many promises as you want really easily, that way you get rid of the callback hell. Best part, you can do promises on Javascript or Node.js
Reference Link https://github.com/kriskowal/q
Hope this helps
Most of the other answers give Promise/A as the answer to your callback woes. This is correct, and will work for you. However I'd like to give you another option, if you are willing to drop javascript as your working language.
Introducing Iced Coffee, a branch of the CoffeeScript project.
With Iced Coffee you would write:
await functionOne data, defer err
await functionTwo data, defer err2
await functionThree data, defer err3
//etc
This then compiles to the CoffeeScript:
functionOne data, (err) ->
functionTwo data, (err2) ->
functionThree data, (err3) ->
//etc
Which then compiles to your Javascript.
functionOne(data, callback(err) {
functionTwo(data, callback(err2) {
functionThree(data, callback(err3) {
//etc
});
});
});
I'm having trouble with how to properly structure a test for my Promise-returning API with Vows, e.g.
topic:function() { return myfunc() { /* returns a Bluebird Promise */ } },
'this should keep its promise':function(topic) {
var myfunc = topic;
myfunc()
.then(function(result) {
assert(false);
})
.catch(function(error) {
assert(false);
})
.done();
}
My vow never fails. This is my first attempt at using vows to test promises. Hoping someone familiar with this will lend a hand.
In advance, thank you.
Enrique
Since unlike libraries like Mocha - Vows does not yet have support for testing promises, we use its regular asynchronous test format that takes callbacks:
topic:function() { return myfunc() { /* returns a Bluebird Promise */ } },
'this should keep its promise':function(topic) {
var myfunc = topic;
myfunc() // call nodeify to turn a promise to a nodeback, we can chain here
.nodeify(this.callback); // note the this.callback here
}
Here is how it would look with mocha:
describe("Promises", function(){
it("topics", function(){
return myfunc(); // chain here, a rejected promise fails the test.
});
})
The following example is using a when js style promise with vows. You should be able to adapt it to whatever flavor of promise you are using. The key points are:
1) Make sure you call this.callback when your promise resolves. I assign 'this' to a variable in the example below to make sure it is properly available when the promise resolves.
2) Call this.callback (see below how this is done with the variable) with an err object and your result. If you just call it with your result, vows will interpret it as an error.
vows.describe('myTests')
.addBatch({
'myTopic': {
topic: function() {
var vow = this;
simpleWhenPromise()
.then(function (result) {
vow.callback(null, result);
})
.catch(function (err) {
vow.callback(result, null);
});
},
'myTest 1': function(err, result) {
// Test your result
}
},
})