I have dynamic array with foldername and creating a zip using zipFolder
I am facing issues as the library is async i.e
zipFolder(source,dest,callback)
Because its a async call it take times and I want to block the next request unless the first one reponse is true,
function (req, res)
var folder = ['N','PP','Y'];//dynamic just an example
for( var i = 0 ; i < folder.length ; i++) {
var source = path.join(basePath,'/download/'+folder[i]);
var dest = path.join(basePath,'/createZip/'+folder[i]+'.zip');
console.log('sending',folders[i])
//this is async
zipFolder(source,dest, function(err) {
if(err) {
console.log('oh no!', err);
} else {
console.log('EXCELLENT');//want to send the second,third,fourth if success
}
});
}
Summary:
output should be like this:
sending file 1 if success/response sending file 2 if success/response
want something like waterfall wait for callback reponse to send the second file
plus I want a method to know the done call back so I can send back the reponse
you can use each as like:
var each = require('async-each-series')
var folder = ['N','PP','Y'];
each(folder, function(items,callback){
//insert each items to db in async way
var source = path.join(basePath,'/download/'+items);
var dest = path.join(basePath,'/createZip/'+items+'.zip');
zipFolder(source,dest, function(err) {
if(err) {
console.log('oh no!', err);
} else {
console.log(items,'inserted')
callback()
}
});
},function(err){
if(err) console.log('error occured')
console.log('all items uploaded');
});
Try with async waterfall method
https://caolan.github.io/async/docs.html#waterfall
Runs the tasks array of functions in series, each passing their
results to the next in the array. However, if any of the tasks pass an
error to their own callback, the next function is not executed, and
the main callback is immediately called with the error
async.waterfall([
function(callback) {
callback(null, 'one', 'two');
},
function(arg1, arg2, callback) {
// arg1 now equals 'one' and arg2 now equals 'two'
callback(null, 'three');
},
function(arg1, callback) {
// arg1 now equals 'three'
callback(null, 'done');
}
], function (err, result) {
// result now equals 'done'
});
UPDATE
in your usecase eachSeries is more applicable.
var folder = ['N','PP','Y'];
async.eachSeries(folder, function(item,callback){
var source = path.join(basePath,'/download/'+item);
var dest = path.join(basePath,'/createZip/'+item+'.zip');
zipFolder(source,dest, function(err) {
if(err) {
console.log('oh no!', err);
callback();
} else {
console.log('EXCELLENT');//want to send the second,third,fourth if success
callback();
}
});
},function(){
console.log('all items uploaded');
}); .
Related
I have this function in the controller
router.post('/', function(req, res, next) {
if (req.user.isPremium == false) {
// Free user - Single report
let website = req.body.website0;
let builtWithCall = `https://api.builtwith.com/free1/api.json?KEY=APIKEY&LOOKUP=${website}`;
let pagespeedCall = `https://www.googleapis.com/pagespeedonline/v4/runPagespeed?url=https://${website}&strategy=mobile&key=APIKEY`;
// curl 'https://www.googleapis.com/pagespeedonline/v4/runPagespeed?url=https://georgiancollege.ca&strategy=mobile&key=APIKEY'
var calls = [];
calls.push(function(callback) {
// First call
https.get(builtWithCall, function(resource) {
resource.setEncoding('utf8');
resource.on('data', function(data) {
// console.log('BuiltWith received', data);
});
});
});
calls.push(function(callback) {
// second call
https.get(pagespeedCall, function(resource) {
resource.setEncoding('utf8');
resource.on('data', function(data) {
// console.log(data);
});
});
});
} else {
// Premium user - comparison report
let websites = [];
}
async.parallel(calls, function(err, results) {
if(err){
console.log(err);
}
console.log('async callback ', results);
res.render('/generated-report', {
title: 'Report',
data: {},
});
});
});
I am trying to run several async API calls at once. The problem is, when I try to run them like this
async.parallel(calls, function(err, results) {
if(err){
console.log(err);
}
console.log('async callback ', results);
res.render('/generated-report', {
title: 'Report',
data: {},
});
});
the console doesn't log anything.
When I do the console log here though
function(callback) {
// second call
https.get(pagespeedCall, function(resource) {
resource.setEncoding('utf8');
resource.on('data', function(data) {
// console.log(data);
});
});
}
it logs the response. The pageSpeed one gets in a weird loop and repeats itself multiple times, but at least it works.
Now what am I doing wrong with the async.parallel part? Also what is this callback in function(callback) {?
EDIT:
This is the new version of the anonymous function:
function(callback) {
// second call
var results;
https.get(pagespeedCall, function(resource) {
resource.setEncoding('utf8');
resource.on('data', function(data) {
results += data;
// console.log(data);
});
resource.on('end', function(data) {
callback(null, data);
});
resource.on('error', function(err) {
callback(err);
});
});
}
You need to call the passed in callback. Looking at your one parallel function you are not calling callback(). I'll assume your resource object has an end & error
function(callback) {
// second call
var results;
https.get(pagespeedCall, function(resource) {
resource.setEncoding('utf8');
resource.on('data', function(data) {
results += data;
// console.log(data);
});
resource.on('end' function() {
callback(null, results);
});
resource.on('error' function(err) {
callback(err);
});
});
}
How async.parallel works is all the functions called must in turn call the passed in callback function; in your case that is callback.
Once each function in the parallel calls callback then and only then will the final function be called, which is the function you defined as function(err, results) {...}.
There is one caveat, if in the callback call you pass non-null for the first argument then that final function will be called immediately where you should handle that error if it happens.
I have an array, and for each row I need to do findIfExist and save into mongodb. The code is here:
router.post('/namespaceUpload', function(req, res,next) {
var data=req.body;
var totalRows=data.allRows.length;
var conceptObject ={};
var existingConcept;
for (var i=0;i<totalRows;i++){
async.series([
conceptPrepare,
conceptFind,
conceptSave,
], function (err, result) {
console.log('kraj');
res.json('Ok');
});
}
function conceptPrepare(callback){
conceptObject.name= data.allRows[i].name;
conceptObject.user= data.userId;
callback();
}
function conceptFind(callback){
namespaces.find({name: conceptObject.name}, function(err, result) {
if (err)
next(err);
else {
if (result.length==0){
console.log('0');
existingConcept='';
} else {
console.log(result.length);
existingConcept=result[0];
}
}
callback();
});
}
function conceptSave(callback){
var namespace = new namespaces();
if (existingConcept==''){
namespace.name=conceptObject.name;
namespace.description=conceptObject.description;
namespace.lastUpdate.user=conceptObject.user;
namespace.save(function(err) {
if (err)
return next(err);
callback();
})
}
}
So I Used async.series, but only last record is written in database as much times as many array members i have. Also, I get an error " Can't set headers after they are sent." Any idea?
You're getting the Can't set headers after they are sent error message because you 're not allowed to return smtg eg : res.send,res.render more than one time but in your for loop, it goes totalRows times
try to return one value at the end of the loop
I want to do async call from my getData function to getImage function but i am unable to get return data from getImage().Since the getData() does't wait for the completion of getImage(),as getImage() has further async db calls and therefore getData() always returns undefined.
What is the best way to do this instead doing nested callbacks?
var getData = function(id){
async.series([
function(callback){
var res = getImages(id);
callback(null, res);
}
],
// optional callback
function(err, results){
if (err) {
console.log("ERROR : " + err);
}else
{
console.log("Result: "+results);
}
});
}
var getImages = function(id){
async.series([
function(callback){
Image.find({id: id }).exec(
function(err, image) {
if (err) {
console.log(err);
callback(err, 0);
}else
{ console.log("Count: "+ image.length);
callback(null, image);
}
});
}
],
// optional callback
function(err, results){
if (err) {
console.log("ERROR : " + err);
}else
{
return results;
}
});
}
getData(1);
As you said you need to wait for getImages() to return, and you do that using promises.
Use any promise library, like q for instance:
var q = require('q')
...
var getImages = function(id){
var deferred = q.defer();
...
//do async logic that that evaluates some res obj you wish to return
db.find(..., function() {
deferred.resolve(res);
}
return deferred.promise;
}
Then, from getData(), you call it in the following matter:
getImages(id).then(
function(res) {
callback(null, res);
},
function(err) {
console.log("error:" + err);
}
);
As you are already using async - just use the waterfall functionality: https://github.com/caolan/async#waterfalltasks-callback
This way you will be able to run functions one after another and wait for the previous to finish, while still getting it's return value.
I have a portion of an Alexa skill running in Lambda that sends a text to a player. I'm using async.waterfall to control the sequence of calling out to a dynamodb table to get the right ARN for that player (where to send the text to), and then after to publish the SMS via AWS SNS. If there is only 1 player in playersToReceive[], it works. However I need it to work for multiple players. To do that, I nested the async.waterfall inside async.forEachSeries (also tried forEach), but I have the callback structure wrong. I think I need to apply the logic of this answer, but I'm new to much of this and struggling.
async.forEachSeries( playersToReceive, // array of items
function(receivingPlayer, callback){
async.waterfall([
function (callback) {
session.attributes.phoneKey = receivingPlayer;
callback(null);
},
function (callback) {
playerStorage.loadPlayer(session, function (newLoadedPlayer) {
if (newLoadedPlayer == 'playerNotFound' || newLoadedPlayer == 'errorLoadingPlayer') {
problems = true; // set problems flag for later
callback(null);
} else {
var ARNtoSend = newLoadedPlayer.data.TopicARN.S;
callback(null, ARNtoSend);
};
})
},
function (ARNtoSend, callback) {
playerSMS.publishSMS(ARNtoSend, textToSend, function (success) {
if (success == false) {problems = true}; // set problems flag for later
callback(null);
})
}
], function (err, result) {
if (err) console.log(err, "SMS text had a problem sending.");
if (!err) console.log(null, "SMS text was successfully sent.");
});
callback();
},
function(err){
// All tasks are now complete
speechText = 'OK, text sent.';
if (problems == true) {
speechText += ' . But there was a problem sending it to some players.'
}
response.tell(speechText);
}
);
I figured it out - the outer callback needs to go inside the closing function of waterfall. With that change made it works:
async.forEachSeries( playersToReceive, // array of items
function(receivingPlayer, callback){
async.waterfall([
function (callback) {
session.attributes.phoneKey = receivingPlayer;
callback(null);
},
function (callback) {
playerStorage.loadPlayer(session, function (newLoadedPlayer) {
if (newLoadedPlayer == 'playerNotFound' || newLoadedPlayer == 'errorLoadingPlayer') {
problems = true; // set problems flag for later
callback(null);
} else {
var ARNtoSend = newLoadedPlayer.data.TopicARN.S;
callback(null, ARNtoSend);
};
})
},
function (ARNtoSend, callback) {
playerSMS.publishSMS(ARNtoSend, textToSend, function (success) {
if (success == false) {problems = true}; // set problems flag for later
callback(null);
})
}
], function (err, result) {
if (err) console.log(err, "SMS text had a problem sending.");
if (!err) console.log(null, "SMS text was successfully sent.");
callback();
});
},
function(err){
// All tasks are now complete
speechText = 'OK, text sent.';
if (problems == true) {
speechText += ' . But there was a problem sending it to some players.'
}
response.tell(speechText);
}
);
Probably just a basic concept of async. I'd like to check on how we can pass the array variable from mongo result(docs) to the second function. In below code the second console.log doesn't give any output.
// Retrieve
var db = require('monk')('localhost/db');
var async = require('async');
console.log('start');
async.waterfall([
function(callback) {
var test = db.get('test');
test.find({}, function(err, docs) {
console.log(docs); //OUTPUT OK
callback(docs);
});
},
function(docs, callback) {
console.log(docs); //NO OUTPUT
}
])
To give you a better understanding of the using multiple callbacks with the async module, let's illustrate this with an example from Seven Things You Should Stop Doing with Node.js of multiple operations with callbacks to find a parent entity, then find child entities that belong to the parent:
methodA(function(a){
methodB(function(b){
methodC(function(c){
methodD(function(d){
// Final callback code
})
})
})
})
With the async module, you can either use the series method to address the use of callbacks for nesting code of multiple methods which may result in Callback Hell:
Series:
async.series([
function(callback){
// code a
callback(null, 'a')
},
function(callback){
// code b
callback(null, 'b')
},
function(callback){
// code c
callback(null, 'c')
},
function(callback){
// code d
callback(null, 'd')
}],
// optional callback
function(err, results){
// results is ['a', 'b', 'c', 'd']
// final callback code
}
)
Or the waterfall:
async.waterfall([
function(callback){
// code a
callback(null, 'a', 'b')
},
function(arg1, arg2, callback){
// arg1 is equals 'a' and arg2 is 'b'
// Code c
callback(null, 'c')
},
function(arg1, callback){
// arg1 is 'c'
// code d
callback(null, 'd');
}], function (err, result) {
// result is 'd'
}
)
Now going back to your example, using the async waterfall method you could restructure your code to
async.waterfall([
function(callback) {
// code a
var test = db.get('test');
test.find({}, function(err, docs) {
if(err) callback(err);
console.log(docs); // OUTPUT OK
callback(null, docs);
});
}], function(err, result) {
if(err) callback(err);
console.log(result); // OUTPUT OK
}
])
You must always handle your errors. Try this!
// Retrieve
var db = require('monk')('localhost/db');
var async = require('async');
console.log('start');
async.waterfall([
function(callback) {
var test = db.get('test');
test.find({}, function(err, docs) {
if(err){
console.log(err);
callback(err);
}
else if(docs){
console.log(docs);
callback(null,docs);
}
else{
console.log("No error no docs");
callback(null);
}
});
},
function(err, docs) {
if(err)
console.log(err);
else{
console.log(docs);
}
}
])