Im doing a asynchronous process with node.js. using promises. My code is like this:
var net = require('net');
var sqlite3 = require('sqlite3').verbose();
var db = new sqlite3.Database('MyBBDD.db');
var net = require('net');
var Q = require("q");
var firstFunction = function(v_user, v_mystring){
var deferred = Q.defer();
var mi;
stmt = db.prepare("SELECT text1 FROM my_table WHERE user = ?");
stmt.bind (v_user);
stmt.get(function(error,row){
if(!error && row){
deferred.resolve({string: v_mystring, query: row.text1});
}
deferred.reject(new Error(error));
});
return deferred.promise;
};
var secondFunction = function(result){
console.log(result.string);
console.log(result.query);
};
firstFunction('user000','Hello').then(secondFunction);
All in my code work fine but now, I want to concatenate in secondFunction my string received from firstFunction with other string for example "MyNewString".
Somebody know how can I solve it? Can I send "MyNewString" from my firstFunction to my secondFunction?
Thanks in advance.
Best regards.
The best to solve it will be resolve promise with object. Instead of returning just one value - result of querying DB you can return object that covers needed value.
With bind:
function firstFunction(string) {
return Promise.resolve({string: string, query: 'some result of query'})
}
function secondFunction(otherText, result) {
console.log(result.query) // you have still access to result of query
return result.string + otherText
};
firstFunction('foo').then(secondFunction.bind(null, 'bar')).then(console.log);
With closure
function firstFunction(string) {
return Promise.resolve({string: string, query: 'some result of query'})
}
function secondFunction(text) {
return function(result) {
return result.string + text
}
};
firstFunction('foo').then(secondFunction('bar')).then(console.log);
With anonymous function expression
function firstFunction(string) {
return Promise.resolve({string: string, query: 'some result'})
}
function secondFunction(text, otherText) {
return text.string + otherText
};
firstFunction('foo').then(function(result) {
return secondFunction(result, 'bar')
}).then(console.log);
Related
Here, am calling the method to get the Quickblox result in async method. While i am print the value in console i can get it but the application keep loading not return the result.
Server side:
Meteor.methods({
allquickbloxusers_Methods: function(){
var params = {login: ["99999"]};
var asyncCall = QB1.users.get(params, Meteor.bindEnvironment(function(err, QBuser) {
if(QBuser) {
return QBuser;
} else {
return err;
}
}));
var syncCall = Meteor.wrapAsync(asyncCall);
var res = syncCall();
// now you can return the result to client.
return res;
}
});
To use Meteor.wrapAsync you want to pass it the actual function, not the result of a called function. Like so:
Meteor.methods({
allquickbloxusers_Methods: function(){
var params = {login: ["99999"]};
var syncCall = Meteor.wrapAsync(QB1.users.get)
var res = syncCall(params);
// now you can return the result to client.
return res;
}
});
Basically wrapAsync gives you back a new function that you call with the parameters of the original function.
Knowing this, you can make the function even more concise:
Meteor.methods({
allquickbloxusers_Methods: function(){
var params = {login: ["99999"]};
return Meteor.wrapAsync(QB1.users.get)(params)
}
});
Considering that my server.js looks almost like this. Just send you the relevant part. I did not receive anything from the query, I do have data in the database, and "sendNotification" is triggered by the jQuery function in the client. Everything works and since var notis = []; returns an empty value and is what is shows as response. I know I have to debug SQL and that's what I'm going to do but anyway want to be sure of this other things. So my questions are:
1) Is a right syntax for node.js, considering this async behavior? (which I still don't understand )
2) The query always should be inside of the "io.sockets.on('connection')" part?
connection = mysql.createConnection({
host: 'localhost',
user: '',
password: "",
database: 'table' //put your database name
}),
...
connection.connect(function(err) {
// connected! (unless `err` is set)
console.log(err);
});
…
var sqlquery = function(uID,vs){
var notis = [];
connection.query("SELECT * FROM notification WHERE kid = ? AND v = ? ORDER BY id DESC",[uID,vs])
.on("result", function (data){
return notis.push(data);
});
};
io.sockets.on('connection', function(socket) {
...
socket.on("sendNotification", function(data) {
var roomBName = data.room_name.replace("room-",""),
found = [];
var roomSelected = _.find(rooms, function (room) { return room.id == roomBName });
for (var person in people) {
for (var i = 0, numAttending = roomSelected.peopleAttending.length; i < numAttending; i++) {
if (people[person].name == roomSelected.peopleAttending[i]) {
found.push(person);
}
}
}
for (var i = 0, numFound = found.length; i < numFound; i++) {
**result = sqlquery(9,2);**
io.to(found[i]).emit('notification', result);
};
});
Your sqlquery() function will not accomplish anything useful. Because connection.query() is asynchronous, that means it provides the response sometime LATER after sqlquery() has already finished.
The only way in node.js to use an async result is to actually use it in the callback that provides it. You don't just stuff it into some other variable and expect the result to be there for you in other code. Instead, you use it inside that callback or you call some other function from the callback and pass it the data.
Here's one way, you could change your sqlquery() function:
var sqlquery = function(uID, vs, callback){
connection.query("SELECT * FROM notification WHERE kid = ? AND v = ? ORDER BY id DESC",[uID,vs])
.on("result", function (data){
callback(null, data);
});
// need to add error handling here if the query returns an error
// by calling callback(err)
};
Then, you could use the sqlquery function like this:
found.forEach(function(person, index) {
sqlquery(..., function(err, result) {
if (err) {
// handle an error here
} else {
io.to(person).emit('notification', result);
}
});
});
And, it looks like you probably have similar async issues in other places too like in connection.connect().
In addition to #jfriend00, this could be done with new ES6 feature Promise :
var sqlquery = function(uID, vs){
return new Promise(function(resolve, reject){
connection.query("SELECT * FROM notification WHERE kid = ? AND v = ? ORDER BY id DESC",[uID,vs])
.on("result", function (data){
resolve(data);
});
});
};
Now you can use it like :
found.forEach(function(person, index) {
sqlquery(...)
.then(function(result){
io.to(person).emit('notification', result);
});
});
In crawler.js :
var async = require('async');
var request = require('request');
var cheerio = require('cheerio');
var Helper = require('./helper.js');
var helper = new Helper();
var Crawler = function(){
var me = this;
this.listCategory;
this.getCategory = function(){
//1. not use async : test key input is idMenu
helper.categoryInMenu(1, function(err, result){
me.listCategory = result;
});
//2. use async : test key input is idMenu
async.map([1], helper.categoryInMenu, function(err, result){
me.listCategory = result;
//console.log(result);
});
console.log(me.listCategory); // return undefined
}
}
module.exports = Crawler;
When i set me.listCategory = result; (with result != null) then call listCategory in file main.js or console.log it in file crawler.js.
Display in command is undefined.
File main.js
var Crawler = require('./crawler.js');
var snipper = new Crawler();
snipper.getCategory();
console.log(snipper.listCategory);
I try use async module with function map, it not work.
Maybe, i not set me.listCategory = return of function helper.categoryInMenu.
File helper.js
var db = require('mysql');
var config = require('./configLoader.js');
config.load(__dirname+'/config.json');
var Helper = function(){
var me = this;
this.conn = db.createConnection(config.get('db'));
this.menu = function(callback){
me.conn.query("SELECT * FROM `menu`", function(err, rows){
callback(err, rows);
});
}
// return list category with "input" is a idMenu
this.categoryInMenu = function(idMenu, callback){
me.conn.query("SELECT * FROM `category` WHERE idMenu = ?", idMenu, function(err, rows){
callback(err, rows);
});
}
// return config of Category with "input" is a idCategory (Category)
this.dom = function(idCategory, callback){
me.conn.query("SELECT * FROM `category` WHERE id = ?", idCategory, function(err, rows){
callback(err, JSON.parse(rows[0].dom));
});
}
}
module.exports = Helper;
Map in all languages is used to transform the values of iterable into something else, that is why you should return in the callback function and the result will be new array containing only the returned values. If you need to iterate only, you should use arr.forEach (>ES5) or simple for loop.
Your problem is that async.map is asynchronous which means console.log(me.listCategory); is executed before the actual map.
Array.map is actually ES5 standard so you don't need async module to map arrays, also the native map is synchronous.
function ParseOrderSchema(CartItems , callback)
{
var lookup = 0;
var subOrderList = new Array();
for(var i=0;i<CartItems.length;i++)
{
Meal.findOne({ _id: CartItems[i].id }).lean().exec(function (err, meal) {
console.log(CartItems[i]);
//meal.mealQTY = CartItems[i].qty;
var s = new subOrder({ meals: meal, deliveryDate: getMomentDate(0) });
subOrderList.push(s);
if (++lookup == CartItems.length) callback(subOrderList);
});
}
}
At CartItem[i].id it works fine and is able to work fine. But it fails at this line meal.mealQTY = CartItems[i].qty;
It can't recognize CartItems[i] inside the findOne() method.
Because findOne is async, i will always be CartItems.length inside the callback as the for loop runs to completion before any of the findOne callbacks occur.
You can fix this by iterating over CartItems using forEach instead so that each iteration's element is captured in a local function parameter:
function ParseOrderSchema(CartItems, callback) {
var lookup = 0;
var subOrderList = new Array();
CartItems.forEach(function(cartItem) {
Meal.findOne({ _id: cartItem.id }).lean().exec(function (err, meal) {
console.log(cartItem);
meal.mealQTY = cartItem.qty;
var s = new subOrder({ meals: meal, deliveryDate: getMomentDate(0) });
subOrderList.push(s);
if (++lookup == CartItems.length) callback(subOrderList);
});
});
}
ive been trying to use node.js to iterate through an array of cities and make an iterative request to google for directions on each (i then JSON.parse to abstract the drive times). I need to find a way to do this synchronously as otherwise i will just be requesting all the info from google on each city at once. I found a good pattern to use at http://tech.richardrodger.com/2011/04/21/node-js-%E2%80%93-how-to-write-a-for-loop-with-callbacks/ but cannot get the callback to work. As you can see, im using a 'show' function to test the same. My code is as follows:
var request = require('request');
var fs = require('fs');
var arr = ['glasgow','preston','blackpool','chorley','newcastle','bolton','paris','york','doncaster'];
//the function I want to call on each city from [arr]
function getTravelTime(a, b,callback){
request('https://maps.googleapis.com/maps/api/directions/json?origin='+a+'&destination='+b+'®ion=en&sensor=false',function(err,res,data){
var foo = JSON.parse(data);
var duration = foo.routes[0].legs[0].duration.text;
console.log(duration);
});
};
function show(b){
fs.writeFile('testing.txt',b);
};
function uploader(i){
if( i < arr.length ){
show( arr[i],function(){
uploader(i+1);
});
}
}
uploader(0)
The problem I have is that only the first city from the array is output and the callback/iteration never proceeds. Any ideas where im going wrong please?
Thanks for the pointers, was clearly down to my poor understanding of callbacks in javascript. Just reading JavaScript patterns by O'Reilly and hit the 'Callback pattern' sections - doh!
For anyone who doesn't know, this is how the code will work:
var arr = ['glasgow','preston','blackpool','chorley','newcastle','bolton','paris','york','doncaster'];
function show(a,callback){
console.log(a);
callback();
}
function uploader(i){
if( i < arr.length ){
show(arr[i],
function(){
uploader(i+1)
});
};
}
uploader(0)
I was also facing issues like this, so I've written a recursive callback function which will act as a for loop but you can control when to increment. The following is that module, name as syncFor.js and include this in your program
module.exports = function syncFor(index, len, status, func) {
func(index, status, function (res) {
if (res == "next") {
index++;
if (index < len) {
syncFor(index, len, "r", func);
} else {
return func(index, "done", function () {
})
}
}
});
}
//this will be your program if u include this module
var request = require('request');
var fs = require('fs');
var arr = ['glasgow', 'preston', 'blackpool', 'chorley', 'newcastle', 'bolton', 'paris', 'york', 'doncaster'];
var syncFor = require('./syncFor'); //syncFor.js is stored in same directory
//the following is how u implement it
syncFor(0, arr.length, "start", function (i, status, call) {
if (status === "done")
console.log("array iteration is done")
else
getTravelTime(arr[i], "whatever", function () {
call('next') // this acts as increment (i++)
})
})
function getTravelTime(a, b, callback) {
request('https://maps.googleapis.com/maps/api/directions/json?origin=' + a + '&destination=' + b + '®ion=en&sensor=false', function (err, res, data) {
var foo = JSON.parse(data);
var duration = foo.routes[0].legs[0].duration.text;
callback(); // call the callback when u get answer
console.log(duration);
});
};