My friend and I have been struggling with Node.js callbacks since yesterday. We have the following function:
// helperFunction.js
function foo(param) {
request.get({
url: <url>
headers: {<headers>}
}, (err, response, data) => {
array = []
obj.forEach(function (entry) {
// do stuff with array
};
});
return array;
});
}
module.exports.foobar = foo;
then we call that from our app.js.
Since yesterday, we have updated the code to wait for the callback by using a function, like so:
// app.js
//var bar = require('./helperFunction');
//console.log(helperFunction.foobar('param')); // prints undefined
function bar(){
console.log('Log something')
}
foo(bar);
but we don't know how to pass the parameter to foo. I tried to add param (which is a string) to bar but it doesn't work.
For the record, I'm aware of other posts such as this, but I cannot make it work on my code.
In foo you just add a callback parameter and instead of returning you call this function. As a convention, the first parameter of the callback function is the error object. If no error occurred, this object is null and the following parameters are the actual result of the function. Your code didn't include error handling so I added it. If error exists you won't receive any data and foo can't calculate whatever it tries to calculate. In this case, foo should either try to solve the problem itself or propagate the error to its caller.
function foo(param, cb) {
request.get({
url: <url>
headers: {<headers>}
}, (err, response, data) => {
if (err) {
return cb(err);
}
array = []
obj.forEach(function (entry) {
// do stuff with array
};
});
cb(null, array);
});
}
function bar(err, data){
console.log('Log something')
}
foo('some param', bar);
Pass a function to foo. Something like:
foo(() => bar("Hi, I'm something"));
function foo(fn, err) {
if (!err && fn instanceof Function) {
fn();
}
}
function bar(someThing){
console.log(`Log ${someThing}`);
}
Related
I have a function that I'm trying to call and basically force it to wait for a response before continuing onto the next thing.
I have two functions, both are asynchronous.
The first one looks something like this, with all parameters that begin with an '_' to be used as callbacks:
async function formatJson(input, _sendToThirdParty, _handleLogs, _setDimensions)
{
...do some work here to format the payload
if(onlineConnectionRequired)
{
_setDimensions(itemToUpdate, object);
}
else {
// Do non-online based transformations here
}
...do more work after the above
}
Basically from that, I'm trying to call this method setDimensions which looks like this:
async function setDimensions(itemToUpdate, object) {
try
{
if(itemToUpdate != null)
{
console.log("Loading dimensions");
await Promise.resolve(function() {
ns.get(`inventoryItem/${object['Item ID']}?expandSubResources=true`)
.then((res) => {
console.log("Inventory Item Loaded. Updating dimensions...");
itemToUpdate.consignments.push(
{
consignmentID: object.conID,
barcode: object.barcode,
itemID: '', // leaving as empty for now
width : res.data.custitem_width,
length : res.data.custitem_length,
height : res.data.custitem_height,
weight : res.data.custitem_weight,
fragile: object.fragile === 'T' ? 1 : 0,
description: object.description
}
);
console.log("Dimensions Finalised");
})
});
}
}
catch(err)
{
console.log(err);
const message = `Error attempting to set the dimensions for ${object['Item ID']}`;
console.log(message);
throw new Error(message);
}
}
The problems I'm coming across are:
The code from the first method continues on before waiting for the promise to resolve, but I need it to wait so I can fully finish building up the payload before it continues on doing the next bits
If I try and include the await keyword before my call to _setDimensions(...) in the first method, I get an error "SyntaxError: await is only valid in async function", but I would've thought that it was an async function?
If anyone could help, that would be incredibly appreciated! Thank you!!
The correct design of your functions are below:
formatJson(input, (err, value) => {
if(err) {
// Error handler goes here
//console.log(err);
throw err;
} else {
// Implementation for working with returned value
console.log(value);
}
});
function formatJson(input, callback)
{
//...do some work here to format the payload
if(onlineConnectionRequired)
{
setDimensions(itemToUpdate, object)
.then((updatedItem) => {
// Implement anything here to work with your
// result coming from setDimensions() function
//console.log(updatedItem);
// Callback with null error and updatedItem as value
callback(null, updatedItem);
})
.catch((err) => {
// Callback with err object and null value
callback(err, null);
});
}
else {
// Do non-online based transformations here
}
//...do more work after the above
}
function setDimensions(itemToUpdate, object) {
try
{
if(inventoryItemID != null)
{
console.log("Loading dimensions");
return new Promise(function(resolve, reject) {
ns.get(`inventoryItem/${object['Item ID']}?expandSubResources=true`)
.then((res) => {
console.log("Inventory Item Loaded. Updating dimensions...");
itemToUpdate.consignments.push(
{
consignmentID: object.conID,
barcode: object.barcode,
itemID: '', // leaving as empty for now
width : res.data.custitem_width,
length : res.data.custitem_length,
height : res.data.custitem_height,
weight : res.data.custitem_weight,
fragile: object.fragile === 'T' ? 1 : 0,
description: object.description
}
);
console.log("Dimensions Finalised");
resolve(itemToUpdate);
})
.catch((err) => reject(err));
});
}
}
catch(err)
{
console.log(err);
const message = `Error attempting to set the dimensions for ${object['Item ID']}`;
console.log(message);
throw new Error(message);
}
}
Mistakes in your code:
Your formatJson function had async keyword but your formatJson function had callback functions named _sendToThirdParty, _handleLogs, _setDimensions. There are 3 types of implementation to create asynchronous codes. You can use callbacks, Promises or async/await. But Promises and async/await are the same except their uses cases and syntaxses. When you define a function as async fn() {...} it basically return a new Promise, so it is equal saying fn() { return new Promise(); }. Functions with callbacks have a shape like function(params, callback) { callback(cbParams); } you can use call callback function in a several branches in your function. But you only have single callback function, your code had 3 callback functions. Also note that functions with callback don't have async keyword. This is not valid because as I mentioned earlier, an async function will return a Promise. So you should not (but you can) define a function as async function(params, callback) like you did in your first method. This is definition is not wrong and it can work but it's not valid.
Your second method was an async function which was returning nothing. So I have changed it to normal function with returning a Promise.
Is the formatJson method beeing called inside an async method? It need to be, and before _setDimensions you need to add a await keyword.
And, as Daniel said, use the promise constructor.
I am trying to make synchronous call to functions in my node js code.
I am calling my functions like this
set_authentication();
set_file();
function set_authentication(){
---
callback function
---
}
I want that my set_authentication() function should execute first completely and then set_file() should start execution but set_file() function start executing before the callback of set_authentication().
I have tried this using async also like
async.series(
[
// Here we need to call next so that async can execute the next function.
// if an error (first parameter is not null) is passed to next, it will directly go to the final callback
function (next) {
set_looker_authentication_token();
},
// runs this only if taskFirst finished without an error
function (next) {
set_view_measure_file();
}
],
function(error, result){
}
);
but it also doesn't work.
I tried promise also
set_authentication().then(set_file(),console.error);
function set_authentication(){
---
callback function
var myFirstPromise = new Promise((resolve, reject) => {
setTimeout(function(){
resolve("Success!");
}, 250);
});
---
}
here I am getting this error:- Cannot read property 'then' of undefined.
I am new to node and js.
You need to return Promise, because you call .then method of a returned promise:
set_authentication().then(set_file);
function set_authentication() {
return new Promise(resolve => { // <------ This is a thing
setTimeout(function(){
console.log('set_authentication() called');
resolve("Success!");
}, 250);
});
}
function set_file(param) {
console.log('set_file called');
console.log(
'received from set_authentication():', param);
}
If set_authentication is async func, you need to pass set_file as callback to set_authentication function.
You may also consider to use promises as you wrote, but you need to implement it before you start chaining.
use async.auto like this:
async.auto(
{
first: function (cb, results) {
var status=true;
cb(null, status)
},
second: ['first', function (results, cb) {
var status = results.first;
console.log("result of first function",status)
}],
},
function (err, allResult) {
console.log("result of executing all function",allResult)
}
);
I'm chaining 3 ajax requests with to a RESTful endpoint:
PUT some data (return {} and OK)
GET the data I just put
show data
I've set up a chain of promises using .then(). But the request does not happen in the expected order (1,2,3) but rather (2,1) and starting with a OPTIONSrequest.
Why are they not happening in the expected order?
How can I ensure the correct sequential order?
var _id = x;
function doReqs() {
putData(_id, data)
.then(getData(_id))
.then(showData);
}
// returns empty object {}
function putData(id, data) {
return $.ajax({
method: 'PUT',
url: http://xxx,
contentType: 'application/json'
});
}
// returns JSON {"data": {"xx": "xx}}
function getData(id) {
return $.ajax({
method: 'GET',
url: http://xxx
});
}
function showData(data) {
console.log(data);
}
In this code:
function doReqs() {
putData(_id, data)
.then(getData(_id))
.then(showData);
}
The .then(getData(_id)) part is just wrong. It's wrong for two reasons.
.then() is supposed to be passed a function reference. When you pass getData(_id), you are executing that function immediately and passing the return value from that function (which is a jqXHR object) to .then(). That's not what you're supposed to pass to .then().
Because you're executing getData(_id) IMMEDIATELY, it will not execute properly in the promise chain sequence.
Remember, any time you pass a func() with the parens after it as an argument, it executes that function immediately and passes it's return value as the argument. That is NOT what you want with .then() for the above reasons.
If you're trying to control what is being passed to getData(), then you can either make sure the right thing is returned from putData() because that's what will be passed to getData() or you can make a stub function that will pass the right thing:
function doReqs() {
putData(_id, data)
.then(function() {
return getData(_id);
})
.then(showData);
}
Or, you could do it this way:
function doReqs() {
putData(_id, data)
.then(getData.bind(null, _id))
.then(showData);
}
Or, since the resolved value of putData() is what will be passed as an argument to the next step in the promise chain (which is getData), you could do this:
function putData(id, data) {
return $.ajax({
method: 'PUT',
url: http://xxx,
contentType: 'application/json'
}).then(function() {
// make sure putData passes the id to the next step in the chain
return id;
});
}
function doReqs(id) {
putData(id, data)
.then(getData)
.then(showData);
}
Here's a working example of chaining in action:
function delay(t, val) {
return new Promise(function(resolve) {
setTimeout(resolve.bind(null, val), t);
});
}
function first(arg) {
console.log("running first..., arg = ", arg);
return delay(500, 10);
}
function second(arg) {
console.log("running second..., arg = ", arg);
return delay(100, 100);
}
function third(arg) {
console.log("running third..., arg = ", arg);
}
first(1).then(second).then(third);
I'm getting "client is not defined". I can see client is not defined for the function but I'm not sure how to pass in client into the async. I don't think I'm actually returning the value of the each correct to pg_commit either or am I?
Basically I want to have an array of queries and loop over them and make them queries and then when all done commit those as a transaction.
var pg_conn_str = "postgres://postgres:5432#localhost/test2";
var pg = require ('pg');
var rollback = function (client, done) {
client.query ('ROLLBACK', function (err) {
return done (err);
});
};
var queries = ["INSERT INTO foo (bar) VALUES (4)",
"INSERT INTO foo (bar) VALUES (5)"];
pg.connect (pg_conn_str, function (err, client, done) {
if (err) throw err;
client.query ('BEGIN', function (err) {
if (err) return rollback (client, done);
process.nextTick (function() {
if (err)
console.log (err);
async.each (queries, pg_commit, function () {
client.query ('COMMIT', done);
console.log ('done');
});
}); //nextTick
}); //begin
}); //connect
function pg_commit (val) {
client.query (val, function (err) {
if (err) return rollback (client, done);
});
return (val);
}
The problem is that client variable is defined only within the callback function called by pg.connect method (as its param). But in pg_commit this name is meaningless - as this function is not defined within the scope of the callback function and doesn't have access to its name bindings.
Hence you have two solutions: either move pg_commit definition into the callback...
function (err, client, done) {
if (err) throw err;
var pg_commit = function(val) { client.query(val, function(err) { ... }};
// ...
... or leave it where it is now, but change it into a function generator:
function pg_commit_generator(client) {
return function(val) { client.query(val, function(){}); /*...*/ }
}
... then use this generator to create a function called by async.each:
async.each(queries, pg_commit_generator(client), function() {...});
Alternatively, you can just supply into async.each the anonymous function that calls pg_commit:
async.each(queries, function(val) { return pg_commit(val, client); }, ...);
... and adjust pg_commit accordingly (so it'll take client as a second param).
How to get the client method.call to wait for an asynchronous function to finish? Currently it reaches the end of the function and returns undefined.
Client.js
Meteor.call( 'openSession', sid, function( err, res ) {
// Return undefined undefined
console.log( err, res );
});
Server.js
Meteor.methods({
openSession: function( session_id ) {
util.post('OpenSession', {session: session_id, reset: false }, function( err, res ){
// return value here with callback?
session_key = res;
});
}
});
Recent versions of Meteor have provided the undocumented Meteor._wrapAsync function which turns a function with a standard (err, res) callback into a synchronous function, meaning that the current Fiber yields until the callback returns, and then uses Meteor.bindEnvironment to ensure that you retain the current Meteor environment variables (such as Meteor.userId()).
A simple use would be as the following:
asyncFunc = function(arg1, arg2, callback) {
// callback has the form function (err, res) {}
};
Meteor.methods({
"callFunc": function() {
syncFunc = Meteor._wrapAsync(asyncFunc);
res = syncFunc("foo", "bar"); // Errors will be thrown
}
});
You may also need to use function#bind to make sure that asyncFunc is called with the right context before wrapping it.
For more information see: https://www.eventedmind.com/tracks/feed-archive/meteor-meteor-wrapasync
I was able to find the answer in this gist. In order to run asynchronous code from within a method.call you use Futures which forces your function to wait.
var fut = new Future();
asyncfunc( data, function( err, res ){
fut.ret( res );
});
return fut.wait();
Update: Sorry, I should have read the question more carefully. It looks like this question was also asked and answered here.
Apart from futures, another pattern to consider might be updating another model with the data returned from the asynchronous call and then subscribing to that model's changes.
From the meteor.call documentation it looks like the the result argument (err, res) of your callback function should contain the output of your openSession function. But you aren't returning any values from your openSession function so the return value is undefined.
You can test this:
Client:
Meteor.call('foo', function(err, res) {
console.log(res); // undefined
});
Meteor.call('bar', function(err, res) {
console.log(res); // 'bar'
});
Server:
Meteor.methods({
foo: function() {
var foo = 'foo';
},
bar: function() {
var bar = 'bar';
return bar;
}
});