I'm trying to excute async-waterfall to get api result and save it to json then save it to database,
thats snippet of my code
Please Help!
async.waterfall([
function getBook(cbAsync) {
books.search(query, (err, result) => {
if (err)
cbAsync(err)
res.json(result)
})
},
function saveToJson(saveToJsonCb, cbAsync) {
jsonfile.writeFile(file, result, (err) => {
if (err)
cbAsync(err)
})
},
function SaveToDb(saveCb, saveToJsonCb, cbAsync) {
const book = {
title: res.body.title,
authors: [res.body.authors],
description: res.body.description
}
//save the bookInfo to db
book.save( (err) => {
if (err)
cbAsync(err)
console.log('Book added!')
})
}
], function asyncComplete(err) {
if (err) {
console.warn('Error')
} else {
console.info('Task complete with success')
}
})
The first task only calls the callback if it encounters an error, which in a perfect condition, will never be called.
Note:
1. Every 'task' must call a callback.
2. Make sure that the callback is called before each function finishes.
Follow the pattern indicated in the Async Documentation
http://caolan.github.io/async/docs.html#waterfall
Related
I am trying to implement a decorator on the fs.readFile function. Instead of the regular error and data params, this version should take two callbacks as arguments (both after the file name) – one to be called on success and one to be called on failure. Both callbacks only have one parameter (the data read from the file or an error object). The actual implementation simply calls fs.readFile.
I can't figure out why this isn't working, and what I'm doing wrong. Please help me debug this. Thank you.
function myReadFile(fileName, successFn, errorFn) {
fs.readFile(fileName,'utf8', function read(errorFn, successFn) {
if (errorFn) {
errorFn();
}
else {
successFn();
}
});
}
fs.readFile callback does not return an errorFn or successFn.
Example from Node.js fs docs:
fs.readFile('/etc/passwd', (err, data) => {
if (err) throw err;
console.log(data);
});
Instead you can pass the err object into your errorFn and same for success with data.
function myReadFile(fileName, successFn, errorFn) {
fs.readFile(fileName,'utf8', function read(err, data) {
if (err) return errorFn(err);
return successFn(data);
});
}
Alternatively you could turn it into a Promise like so:
function myReadFile(fileName) {
return new Promise((resolve, reject) => {
fs.readFile(fileName,'utf8', function read(err, data) {
if (err) return reject(err);
return resolve(data);
});
});
}
//usage:
myReadFile('/some/file')
.then(data => {
//data here
})
.catch(err => {
//error here
});
I'm using NodeJS and mysql2 to store data in my database. but their are times when I need to perform database saves synchronously, like this example:
if(rent.client.id === 0){
//Save client
connection.query('INSERT INTO clients (name, identity, licence, birthDate, address, phoneNumber, email) VALUES (?,?,?,?,?,?,?)',
[/*Array of values*/],
function (err, results) {
if (err) throw err;
//Retrieve client id to use it in the next database save
rent.client.id = results.insertId;
})
}
//Save rent
connection.query('INSERT INTO rents (deliveryDate, returnDate, gasLevel, deliveryPoint, deliveryPrice, unitPrice, state, clientID, carID) VALUES (?,?,?,?,?,?,?,?,?)',
[/*Array of values that contain the last inserted id clients*/],
function (err, results) {
if (err) throw err;
console.log('rent saved', results);
})
So how can I perform these two database saves synchronously. I don't think that doing it in the following manner is good code:
connection.query(queryString,
[queryValues],
function (err, results) {
if (err) throw err;
connection.query(queryString,
[queryValues],
function (err, results) {
if (err) throw err;
console.log('rent saved', results);
})
})
So what kind of solutions do you propose?
I don't think that doing it in the following manner is good code
It isn't, but only because of the
if (err) throw err;
part, which will not do anything useful. (It certainly won't make your function making these query calls throw an exception; it can't, you function has already returned. All it does is throw an exception from the callback; query probably ignores it.)
Other than that, it's the correct way to do this with NodeJS-style callbacks. More specifically:
function myFunctionToDoThisWork(callback) {
connection.query(queryString1,
[queryValues1],
function (err, results) {
if (err) {
callback(err);
return;
}
connection.query(queryString2,
[queryValues2],
function (err, results) {
if (err) {
callback(err);
return;
}
console.log('rent saved', results);
});
});
}
There are couple of things you can do to make that code easier to maintain:
One is to use promises, which you can use on any vaguely-recent version of Node (or via an npm module). First we'd give ourselves a Promise-enabled version of query. In Node v8 and above, you can do that like this:
const promisify = require("utils").promisify;
// ...
const queryPromise = promisify(connection.query.bind(connection));
Alternately there's the promisify npm module, or really this basic version is really trivial:
function promisify(f) {
return function() {
var t = this;
return new Promise(function(resolve, reject) {
var args = Array.prototype.slice.call(arguments);
args.push(function(err, data) {
if (err) {
reject(err);
} else {
resolve(data);
}
});
f.apply(t, args);
});
};
}
Then:
function myFunctionToDoThisWork() {
return queryPromise(queryString1, [queryValues1])
.then(() => {
return queryPromise(queryString2, [queryValues2]);
})
.then(() => {
console.log('rent saved', results);
});
});
}
then consume it via:
myFunctionToDoThisWork().then(/*...*/).catch(/*...*/);
On Node v8 and higher, you can take that further with async and await:
async function myFunctionToDoThisWork() {
await queryPromise(queryString1, [queryValues1]);
await queryPromise(queryString2, [queryValues2]);
console.log('rent saved', results);
}
If you call it from an async function, you'd consume it via await. If calling it from a non-async function, you consume it just like the promise version above (via then).
request('GET', url_ws).done((res) => {
if (res.statusCode==200) {
parseString(res.getBody(),{explicitArray:false}, function (err, result) {
pdfAnnotations=result['root']['element'];
console.log(pdfAnnotations);//show value//second
});
}
});
console.log(pdfAnnotations);//display "undefined"//first
fn_work(pdfAnnotations)
Hello, i have to work with variable loaded from web service, but when my function starts, variable is 'undefined'
You need to call your function after parseString() is done:
request('GET', url_ws).done(res => {
if (res.statusCode == 200) {
parseString(res.getBody(), { explicitArray: false }, function (err, result) {
const pdfAnnotations = result['root']['element']
doSomething(pdfAnnotations)
})
}
})
this is normal because the code is executed asynchronosly, it makes the request and then executes fn_work right after that, while fetching data from url_ws , then when it gets the data, it moves on to ParseString and so on,
the easy way is to move fn_work(pdfAnnontaions) inside the callback of the ParseString like so
request('GET', url_ws).done((res) => {
if (res.statusCode==200) {
parseString(res.getBody(),{explicitArray:false}, function (err, result) {
pdfAnnotations=result['root']['element'];
fn_work(pdfAnnotations);
});
}
});
i would recommend using promises or async/await , check these out :
https://blog.risingstack.com/mastering-async-await-in-nodejs/
https://www.valentinog.com/blog/http-requests-node-js-async-await/#Making_HTTP_requests_with_Nodejs_the_request_module
I am trying to call two functions and pass the output of the first function as a parameter into the second.
Function 1:
module.exports.getAllStatisticsByUserId = function(id, callback){
User.findById(id, (err, user) =>{
if(err)
throw err;
if(user)
callback(null, user.statistics);
});
}
Function 2:
module.exports.getGameByStatisticsId = function(id, callback){
Statistics.findById(id, (err, statistics) =>{
if(err)
throw err;
if(statistics)
callback(null, statistics.game);
});
};
I am trying to execute the second method by passing the output of the first method as a parameter but the asynchronous nature of javascript is messing it up. I have tried implementing promises to no avail.
Can anyone suggest some good javascript practices to deal with calling functions asynchronously when they need each other? Any help would be appreciated.
After fixing the issue I mentioned above, you can call them in sequence like this:
module.exports.getAllStatisticsByUserId = function(id, callback){
User.findById(id, (err, user) =>{
if(err) callback(err);
if(user) callback(null, user.statistics);
});
};
module.exports.getGameByStatisticsId = function(id, callback){
Statistics.findById(id, (err, statistics) =>{
if(err) callback(err);
if(statistics) callback(null, statistics.game);
});
};
someService.getAllStatisticsByUserId(id, (err, statistics) => {
if (err || !statistics) {
// handle error
return;
}
someService.getGameByStatisticsId(statistics.id, (err, game) => {
if (err || !game) {
// handle error
return;
}
// handle game
});
});
However, as noted in Mongoose documentation:
When a callback function is not passed, an instance of Query is returned, which provides a special query builder interface.
A Query has a .then() function, and thus can be used as a promise.
So you can simply rewrite the calls like this:
someService.getAllStatisticsByUserId(id).then(statistics =>
someService.getGameByStatisticsId(statistics.id)
).then(game => {
// handle game
}).catch(err => {
// handle error
});
or convert it into an async/await function:
async function getGameByUserId(id) {
try {
const statistics = await someService.getAllStatisticsByUserId(id);
const game = await someService.getGameByStatisticsId(statistics.id);
// handle game
} catch (error) {
// handle error
}
}
Note that an async function always returns a Promise, so you must await it or chain it with a .then() to ensure completion of the query and resolve the returned value, if any.
It looks like you should be able to write:
getAllStatisticsByUserId("me", (err, stats) => {
getGameByStatisticsId(stats.id, (err, game) => {
console.log(game);
});
});
Here's how it would look if these functions returned promises instead.
getAllStatisticsByUserId("me")
.then(stats => getGameByStatisticsId(stats.id))
.then(game => console.log(game))
Even better, if you're able to use a version of Node that supports async/await then you could write.
let stats = await getAllStatisticsByUserId("me");
let game = await getGameByStatisticsId(stats.id);
console.log(game);
This would mean slightly rewriting the original functions (unless User.findById and Statistics.findById already return promises).
module.exports.getAllStatisticsByUserId = function(id, callback){
return new Promise((resolve, reject) => {
User.findById(id, (err, user) =>{
if(err) return reject(err);
return resolve(user.statistics);
});
});
}
I'm making on app for camps where user can come and create their camping experience and comments over it. I try to remove first if any camps there in mongodb, after that to make 3 dummy camps data and then associate comments on it. but it seems always all 3 camps creating first and then comments because of that comments can't be associated with them.
Campground.remove({}, function (err) {
if (err) {
console.log('some error in campground');
}
campdata.forEach(function (seed) {
Campground.create(seed, function (err, createdData) {
if (err) {
console.log('camps not created');
} else {
// create comments
Comment.create({
description: 'this is the best place but wish if there is internet',
author: 'satty'
}, function (err, commentdata) {
if (err) {
console.log(err);
} else {
createdData.comments.push(commentdata);
createdData.save();
console.log(commentdata);
}
});
console.log(createdData);
} //else completed
}); // campground create completed
}); // for each
console.log('removed campgrounds');
}); // campground remove
Remember that Node is asynchronous. forEach runs synchronously, but the functions within are asynchronous — meaning that they are still executing after the forEach loop completes. This is a problem for you because the iterator on forEach has already reached the last element in the array long before the asynchronous comment-adding function executes.
One way to solve this is to use async:
(Removed superfluous code for brevity)
let async = require('async')
Campground.remove({}, function(err) {
async.each(campdata, function(seed, callback) {
Campground.create(seed, function(err, createdData) {
let comment = {
description: 'this is the best place but wish if there is internet',
author: 'satty'
}
Comment.create(comment, function(err, commentdata) {
createdData.comments.push(commentdata)
createdData.save()
callback(err)
})
})
}, function(err) {
// all done!
})
})