I am trying to produce async function by iterating through the array of 'titles' and passing array of function later to async waterfall.
according to docs:
var asyncFunction = [
function(callback){
asyncFunc(1, function(){
callback(null);
});
},
// page 2
function(data, callback){
asyncFunc(2, function(){
callback(null, data);
});
}
]
The first function takes callback as the first parameter, while the second and all subsequent functions function is taking data as the first parameter and callback as the second.
My question how do I create function conditionally based on the index of map iteration?
Below is !non-working code example to give you an idea what I'm trying to accomplish.
Thanks
const asyncFuncs = ['a','b','c'].map( (letter, index) => {
const args = i == 0 ? [callback] : [data, callback]
return function(args...){
asyncFunc(2, function(){
callback(null, data);
});
}
})
async.waterfall(asyncFuncs, (error, result) => {})
Sorry if I was wrong.
I guess that you want to dynamic waterfall functions base on your data.
var yourData = ['a','b','c'];
async.map(yourData, yourAssignFunction, function (err, result) {
if(!err) {
console.log('Success: ' + result);
} else {
console.log('Error: ' + err);
}});
function yourAssignFunction(item, callback) {
if (item === 'a') { // your conditions check here
// use waterfall 1
callback(waterfallFunction1);
} else {
// use waterfall 2
callback(waterfallFunction2);
}
}
function waterfallFunction1(item, callback) {
async.waterfall([
function(cb){
console.log(' -> task1: ', item);
cb(null, item);
},
function(response,cb){
console.log(' -> task2: ', item);
cb(null, item);
}], callback)
}
Related
I am trying to execute following array (avoid callbackHell) of functions(sync/async), in a sequential order, implementing function runCallbacksInSequence (I need to implement my own function to understand how callbacks work and avoid using Async.js).
Here is what I have so far. The function runCallbacksInSequence works well till it gets the same callback cb(null, 'one'); the second time. Ideally if it gets the same callback more than once it should not execute it second time and ignore it.
If you have any ideas let me know how I can implement it. - no promises and async/await
function first(cb) {
setTimeout(function() {
console.log('first()');
cb(null, 'one');
cb(null, 'one');//not supposed to be execute by runCallbacksInSequence
}, 0);
}
function last(cb) {
console.log('last()');
cb(null, 'lastCall');
}
function runCallbacksInSequence(fns, cb) {
fns.reduce(
(r, f) => {
return k => {
return r(() => {
return f((e, x) => {
return e ? cb(e) : k(x);
});
});
};
},
k => {
return k();
}
)(r => {
return cb(null, r);
});
}
fns = [first, last];
runCallbacksInSequence(fns, function(err, results) {
if (err) return console.log('error: ' + err.message);
console.log(results);
});
I'm using the loopback, here while making the update call with list of objects in array.
I get in the callback is already called!
The scene is, I have defined the callback inside the loop, and in the first loop, it is get in called actually.
I am looking for the way where
I should update all list of object in query MySQL plan call.
Inward.updateIsActiveDetails = function(data, callback) {
var id = _.map(data, 'id');
if (id.length > 0) {
_.forEach(id, id => {
console.log('id....:', id)
Inward.updateAll({id}, {
isActive: 0,
}).then(updateresult => {
console.log(updateresult);
// callback(error); showing err with it... (callback already called)
}).catch(function(error) {
callback(error);
});
});
} else {
callback(null, {
success: true,
msg: 'No records to update',
});
}
};
output:
id....: 3
id....: 4
{ count: 1 }
{ count: 1 }
appreciate for right solution
The callback is supposed to be called once, you're calling it in the loop, so it will be called for each iteration of the loop. More than once. The following would be correct if for whatever reason you can't use async/await.
Inward.updateIsActiveDetails = function(data, callback) {
var id = _.map(data, 'id');
var len = id.length;
var resultList = [];
// When you call this function we add the results to our list
// If the list of updates is equal to the number of updates we had to perform, call the callback.
function updateResultList(updateResult) {
resultList.push(updateResult);
if (resultList.length === len) callback(resultList);
}
if (len > 0) {
_.forEach(id, id => {
Inward.updateAll({id}, {
isActive: 0,
})
.then(updateResult);
});
} else {
callback(null, {
success: true,
msg: 'No records to update',
});
}
};
With async/await it would be much shorter.
Inward.updateIsActiveDetails = async function(data) {
const results = [];
for(let i = 0; i < data.length; i++) {
results.push(await Inward.updateById(data[i].id));
}
return results;
}
Here is my final and working answer.
Basically, updateAll query runs once and it will run as an inbuilt query
id: {
inq: _.map(data, 'id'),
}
So, after running that it will update the respective row only! very interesting.
Inward.updateIsActiveDetails = function (data, callback) {
Inward.updateAll({
id: {
inq: _.map(data, 'id'),
},
}, {
isActive: 0,
}, function (error, resultDetails) {
if (error) {
console.log('error', error);
callback(error);
} else {
console.log('resultDetails', resultDetails);
callback(null, resultDetails);
}
});
};
Response.json should execute after foreach loop completes its execution
var todoarr = (req.body.data) ? req.body.data : undefined
todoarr.forEach(function(element) {
if(element.done == true) {
TodoService.removeTodo(element, function(success) {
});
}
});
res.json("success");
You can try to use async.js http://caolan.github.io/async/ .
each method http://caolan.github.io/async/docs.html#each
Or you can try use Promise.all.
For example:
let promiseArr = [];
todoarr.forEach(function(element) {
if(element.done == true) {
promiseArr.push(somePromiseMethod(element));
}
});
//now execute promise all
Promise.all(promiseArr)
.then((result) => res.send("success"))
.catch((err) => res.send(err));
More info here. https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise/all
Some promise example:
function somePromiseMethod(element) {
return new Promise((resolve,reject) => {
TodoService.removeTodo(element, function(success) {
resolve();
});
});
}
Hope this helps.
You can't send multiple responses on single request, the only thing you can do it's a single response with the array of results:
es with async:
const async = require('async')
// FIX ME: this isn't correctly handled!
const todoarr = (req.body.data) ? req.body.data : undefined
let results = []
async.each(todoarr, function(element, callback) {
console.log('Processing todo ' + element)
if(element.done == true) {
TodoService.removeTodo(element, function(err, success) {
if(err){
callback(err)
} else {
results.push(success)
callback(null, success)
}
})
}
}, function(err) {
if(err) {
console.log('A element failed to process', err)
res.status(500).json(err)
} else {
console.log('All elements have been processed successfully')
// array with the results of each removeTodo job
res.status(200).json(results)
}
})
You can send the response inside the callback function of forEach.
Modify your function so that it will call res.json() on the last iteration only.
Example:
var todoarr = (req.body.data) ? req.body.data : undefined
todoarr.forEach(function(element,index) {
if(element.done == true) {
TodoService.removeTodo(element, function(success) {
});
}
if(index==todoarr.length-1){
res.json("success");
}
});
However, it may not be according to coding standards but it can definitely solve the problem.
waterfall function with two calls but the second on is not waiting for the first one to completely finish. The first one has a mongodb.find() call in it.
Here is the async-waterfall function
app.get("/news", function(req, res) {
async.waterfall([
function (callback) {
var blogs = tendigiEngine.getAllBlogs(callback);
callback(null, blogs);
},
function (blogs, callback) {
var array = tendigiEngine.seperateBlogs(blogs, callback);
callback(null, array );
}
], function (err, result) {
// result now equals 'done'
console.log("done");
console.log(result);
});
});
Here are the two functions being called:
getAllBlogs():
exports.getAllBlogs = function() {
Blog.find(function(err, theBlogs){
if(!err) {
return theBlogs;
}
else {
throw err;
}
});
}
seperateBlogs():
exports.seperateBlogs = function(blogs) {
if(blogs.length === 0 ) {
return 0;
}
else {
blogs.reverse();
var blog = blogs[0];
blogs.shift();
var finArray = [blog, blogs];
return finArray;
}
console.log("asdf");
}
It is important that seperateBlogs won't be called before getAllBlogs() has returned theBlogs, but it is being called before the value is returned. I used Async_Waterfall to avoid this problem but it keeps recurring, which means I am using it wrong. What am I doing wrong here and how can I fix it?
Thanks!
Your exported functions are missing the callback parameters:
exports.getAllBlogs = function(cb) {
Blog.find(cb);
};
exports.seperateBlogs = function(blogs, cb) {
if (blogs.length === 0 )
return cb(null, blogs);
blogs.reverse();
var blog = blogs[0];
blogs.shift();
cb(null, [blog, blogs]);
}
Then your main code can be simplified as well:
async.waterfall([
tendigiEngine.getAllBlogs,
tendigiEngine.seperateBlogs
], function (err, result) {
// result now equals 'done'
console.log("done");
console.log(result);
});
This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
Closed 8 years ago.
I have the following function:
module.exports = {
listAccounts: function (callback) {
var acc = [];
var calls = [];
function processAccounts(account, cb) {
client.getAddressesByAccount(account, function (err, addresses) {
console.log('getting account', account);
acc.push({name: account, addresses: addresses});
return cb();
});
}
client.listAccounts(function (err, res) {
if (err)
return callback(err)
else {
for (var key in res) {
calls.push(
function (cb) {
processAccounts(key, cb);
}
);
}
async.parallel(calls, function (err, results) {
console.log(acc);
})
}
})
}
}
What I want to do is to use the for loop in order to push all the functions to the calls array and then run async.parallel to run all the functions.
The problem is, that the calls array looks like this after the for loop finishes:
function (cb) {
processAccounts(key, cb);
},function (cb) {
processAccounts(key, cb);
},function (cb) {
processAccounts(key, cb);
}
It does not translate the key to the actual value but keeps it as key
Use function to create a new scope where the value can be container in the closure.
var res = {a:'hello', b:'howdy'};
var calls = [];
for (var key in res) {
calls.push(
(function(k) {
return function (cb) {
console.log('say: ' + res[k] + ' to ' + cb);
}
})(key)
);
}
calls[0]('you');
calls[1]('me');
prints:
say: hello to you
say: howdy to me
I think what you want here is async.each
async.each(calls, function(key, cb) {
processAccounts(key, cb);
},
function(err){
if (err)
console.log('oh no!', err);
console.log('all accounts processed');
});