This question already has answers here:
How to avoid long nesting of asynchronous functions in Node.js
(23 answers)
Closed 6 years ago.
I'm new in js. In my project I'm using mysql and node js.
Somewhere in controller I need to get some data from differnet models. In php it looks like
function some() {
$user = $user->getOne($id);
$photos = $photos->getOne($user->id);
$posts = $post($user->id, $photo->uid)
}
and I have all this variables in one scope
in node js result of model is async, so it's look like nestings callbacks.
Short example
UserModel.findbyid(result.user_id, function (err, user_data) {
PhotoModel.GetVoteCount(user_data.id, result.id, function (res_count) {
PhotoModel.getWinners(function (err, winners_ar) {
PhotoModel.getweekusers(1, function (result_week) {
response.render('one.twig', {
view_user: request.user,
image: result,
p_user: user_data,
count: res_count,
winners: winners_ar,
week_users: result_week['photos']
});
});
})
});
});
so I have nested callbacks, I feel it's not right way to code, can you explain best practices?
You have two options :
Use async module (async)
Use a library which returns promises (like promise-mysql).
You can take a look at async module
This simplifies what you call "callback hell" through some functions that helps create flows in async code.
Specifically to your case - async.waterfall will do the trick
Use promises,or libraries that will help you use promises.
Related
This question already has answers here:
How to mock functions in the same module using Jest?
(10 answers)
Closed 6 months ago.
Consider this set of code snippets where I want to write unit tests for function A, which internally calls function B during execution. Assume B is a set of API calls for validation that I want to mock return value to true.
But this spyOn method approach does not work, and the API calls in B still get executed. I've seen approaches with mocking the complete module with jest.requireActual(), but they do not seem to work too.
What could be a better way to test such functions without changing a lot in the codebase index.ts?
//index.ts
async function A (a:string, b:string, c:string) {
// code
await B(data);
// code
}
async function B (data:customType) {
//code
//API calls
//code
}
export default {
A,
B }
//index.test.ts
import index from '../index.ts';
describe('Test suit', ()=>{
it('should be a test for function A', async ()=> {
jest.spyOn(index, 'B').mockReturnValue(true);
// code
const result = await index.A('a','b','c');
// code
})
})
There is not really a good way to do it, when the functions are in the same module with jest only.
But you can with Rewire, it's explained in this answer and worked very well in a similar situation for me: https://stackoverflow.com/a/52725067/16068019
Otherwise if it's an option you could move one of your functions to a different module, but that's not always an option.
I'm fairly new to JS especially Node and Express. I am following some tutorials on how to build an API and at the same time learning about JS special features such as let/const/var, arrow functions etc.
In many tutorials I have seen things likes this :
somecode.then((result) => {someothercode})
With: "somecode" being for example a get request
Is "result" the name of the returned value or is it a convention that JS developper use?
By that I mean, does this for example work?
somecode.then((foo) => {someothercode})
Also for req, res variables what does this mean?
app.get("/users/:userId", [
usersController.getById
]);
Here is the getById function (using once again the "result"):
exports.getById = (req, res) => {
userModel.findById(req.params.userId).then((result) => {
res.status(200).send(result);
});
};
the getById method defined in the controller needs (req, res), does that mean, when i call it like the code above, the req and res arguments are implicitly used?
Also it needs a parameter :
req.params.userId
which is in the url of the route, how does it pass to another file?
I have a route.js file that uses a controller.js file that uses a model.js. How does the param go from route to controller?
And it won't work if I change the param name right? for example:
req.params.id
Sorry for long post, I'm trying to understand JS logic to get some good habits and write clean code.
Thanks!
Is "result" the name of the returned value or is it a convention that JS developper use? By that I mean, does this for example work?
From my experience, yes - result is often used. Often times you'll see thing like value, response, but ultimately it can be whatever you define. I would recommend sticking to convention, and also check out the MDN Promise tutorial if you are starting out with understanding NodeJS asynchronous operations.
Also for req, res variables what does this mean?
app.get("/users/:userId", [
usersController.getById
]);
That is a middleware chain. Check out the Express docs for more information.
the getById method defined in the controller needs (req, res), does that mean, when i call it like the code above, the req and res arguments are implicitly used? Also it needs a parameter :
req.params.userId
which is in the url It won't work if I change the param name right? for example:
req.params.id
Yes, that is using a named parameter. Without the full router code, it is hard to know how the getById method is linked to the defined route. The Express routing documentation will likely be a good start on that.
Is "result" the name of the returned value or is it a convention that JS developper use?
result is the name of a new variable you are creating to represent the value passed in from the Promise resolution. Yes, your foo example will work.
(req, res) => {} is the same (mostly) as a function that looks like this:
function getById(req, res) {...}
req, and res are just representational of the values that will be passed to this function. They could just as easily have been called (foo, bar).
It looks like you're struggling with understanding callback functions. Consider the following code then please crack open the source code for the packages that you are using. and it looks like you are using express.js
function something(callback) {
var x = 5;
var y = 'anything';
callback(x, y);
}
something(function(req, res) {
console.log(req);
console.log(res);
});
the something function is created and inside of that function scope, var x and y are created with any type. then when we invoke or use something function we are passing a function as a variable that gets passed in as variable callback then it can be used since it is a function so we call callback with x and y which can literally be any value and for effect, I am passing back a number and a string as req and res.
It's just a convention. Note that the code:
somecode.then((result) => {someothercode});
Is actually:
somecode.then(myFunction);
Since somecode is a Promise, your function may be called with zero or one argument. It is up to you to name this argument:
function myFunction (foo) {
// use foo here
}
somecode.then(myFunction);
Of course, unlike some other languages, javascript does not force you to name your function. You can just use a nameless (anonymous) function:
somecode.then(function(mango) { /* use mango here */ })
Arrow functions is a new syntax allowing you to write anonymous functions in a shorter style (it also behaves slightly differently with regards to scope and the value of this)
Express.js and http.Server
In node's http.Server library and Express.js framework, each server request will call a function you define and pass it two arguments: the request object and the response object. The variables req and res are just conventions people use when writing their own request handler functions. You can name them anything you like. For example you may prefer to use request and response instead or rx and tx:
app.get('/say/hello', (rx, tx) => tx.send('Hello'));
How many arguments do I write a callback function with??
The best way to know is to read the documentation of the module you are using. It is not the only way to know - you can of course read the source code instead. But it is often easier to read the documentation. Because of this, javascript modules tend to have really good documentation (otherwise they would be unusable and ignored by the community).
Express.js will actually pass three arguments to your callback (not two!!) - request, response and next where next is a function you can call if you want Express to continue processing instead of replying to the request. One interesting feature of javascript is that you are allowed to call functions with fewer or more arguments and it is not considered a syntax error:
function example (x) {}
example(); // not an error
example(1); // not an error
example(1,2,3,4); // also not an error
Express uses this feature by always calling your callback with three arguments while allowing you to declare said callback with only two arguments if you don't need the third, next argument.
This question already has answers here:
How to access the correct `this` inside a callback
(13 answers)
Are 'Arrow Functions' and 'Functions' equivalent / interchangeable?
(4 answers)
Closed 4 years ago.
Im exploring vue and new javascript tools - things are a bit convoluted for me, in the sense I struggle to debug and understand what is going on.
I am trying to fetch data via a simple API, using axios as suggested here:
https://v2.vuejs.org/v2/cookbook/using-axios-to-consume-apis.html#Real-World-Example-Working-with-the-Data
turns out, if I use "normal" javascript, data will not be set.
It must have something to do with interpreting "this" element.
Instead, if I use arrows functions, data are set.
data () {
return {
query : {
info : null
}
}
methods : {
test : function(query) {
params = {
origin : '*',
action : 'query',
prop : 'pageimages',
pithumbsize : 400,
pilimit : 'max',
list : 'search',
srsearch : query,
utf8 : true,
format : 'json'
}
axios.get('https://en.wikipedia.org/w/api.php', {
params : params
})
.then(function (response) {
// won't work : this.query.info // undefined
this.query.info = response.data.query.search
})
.then(
// this.query.info is set
response => {
this.query.info = response.data.query.search
}
)
}
}
Can you explain why?
I helped myself with answer in :
What does "this" refer to in arrow functions in ES6? - "this refers to the enclosing context" :
what does this refer to in the pattern then(function(response){ this.[...]) and in the patter then( response => this.[...]) ?
how could I use syntax without arrows functions, and use this referring to the data() model of vue ?
Would you mind to also suggest working practices to keep code clean and simple?
I came from using native javascript and jquery, and despite a bit more verbose, I could understand well what was going on and easily debug.
Instead with these new tools, despite powerful, I am a bit lost for it seems there is an overload of other tools to use to get things done.
While learning I still would prefer to code a bit more verbosely and use web console to debug, instead of nodes plugin and syntax that must be compiled by frameworks.
__
Edited
Upon comments, I tried:
.then(function(response){
// won't work : here thisArg should be the vue app,
// so self.query.info should bind to
// [App data()].query.info
// still, query.info is undefined
// though response was successfully fetched
var self = this
self.query.info = response.data.query.search
})
Edited 2 (Answer)
I found this answer helpful:
https://stackoverflow.com/a/29881419/305883
so I realised above patter was wrong, for writing var self =this within the then() means I am still referenceing the this of the promise object, which is undefined.
so with "normal" functions, I should write something like:
var self = this; // now self is referenced to the external object, the Vue app
fetchResponseFromApi(query)
.then(function(response){
self.query.info = response.data.query.search
})
This answer address my question (no pun for "this" answer, hohoho :).
In other answers I read comments not to pass around the thisArg, like into encapsulated functions:
comments about clarifying about good coding patterns will be of good for others to.
I have some code like this:
function example(data){
asyncFnOne()
.all([asyncFnTwo(), data])
.spread(asyncFnThree)
.done();
};
It doesn't matter what those functions do. The problem I have is that I don't know how asyncFnThree can access both data from asyncFnTwo and from the function parameters. The way I solved the problem is not very readable. Is there a recommended way to do this in a clean way?
A second possible soultion would be
function example(data){
asyncFnOne()
.then(asyncFnTwo)
.then(function(result){
asyncFnThree(result, data);
})
.done();
};
};
But I think it is even less readable.
To use Q.all in the desired way you'd have to do the following:
function example(data) {
return Q.all[
asyncFnOne().then(asyncFnTwo),
data
]).spread(asyncFnThree);
}
Or use a variable for the first part of the chain, so that you don't have to stuff everything in one expression and could use return Q.all([promiseTwo, data])….
The alternative way with the closure is totally fine as well, and rather standard. You might as well use any library that offers some kind of partial application, and use that instead of the function expression.
function example(data) {
return asyncFnOne()
.then(asyncFnTwo)
.then(asyncFnThree.bind(null, data)); // argument order to asyncFnThree: data, result
}
You can use variables as buffers or create some "middle-man" promises. You can find some good examples here: http://pouchdb.com/2015/05/18/we-have-a-problem-with-promises.html?utm_source=javascriptweekly&utm_medium=email
This question already has answers here:
How does the "this" keyword work, and when should it be used?
(22 answers)
Closed 7 years ago.
I'm using Sails.js framework and its great Model's feature, also I'm using the async version it uses as its dependency.
So, in order of explaining a practical scenario: get all comments on songs sung by an artist. I should first query the songs and then, query against comments.
This why I'm using async module, in particular its waterfall function. But as all this code is placed at a Model file, where this refers to the model itself, I got one big doubt:
Will this always refer to the model even when present inside async functions?
This is a code example of what I'm doing:
module.exports = {
connection: 'seasonDB',
attributes: {
reachesMaxFavoriteTeam: function (team) {
var results [];
async.waterfall([
// Get favorite team
function (cb) {
var userTeam = this.userTeam;
UserTeam.findOne({_id: userTeam}, function (err, foundUserTeam) {
if (err)
cb(err,null);
var user = foundUserTeam.user;
User.find({_id: user}, {_id:false, fanOf: true}, function (err, fanOf) {
if (err)
cb(err,null);
cb(null,fanOf.name);
});
});
},
// Check if it reaches a max favorite team error
function (favoriteTeam,cb) {
// If player to be added is from favorite team, it counts.
favoriteCount = team === favoriteTeam ? 1: 0;
// Check if any of added players are from favorite team too.
_.forEach(this.players, function (player) {
var playerTeam = player.team.name;
if (playerTeam === favoriteTeam)
favoriteCount++;
});
return favoriteCount > process.env.CONDITION;
}]);
}
};
So, for example, at the first function in waterfall series I got: var userTeam = this.userTeam;, will this work as expected? Should I take care of something in case of other nested functions?
Why don't you use Promises in those queries will make it so much easier to work with. I wouldn't use async, using Promises should do the job.
The underlying ORM module in Sails is Waterline. You can refer to Chaining waterline calls with Promises for an example or the GitHub page https://github.com/balderdashy/waterline which shows the below example,
User.findOne()
.where({ id: 2 })
.then(function(user){
var comments = Comment.find({userId: user.id}).then(function(comments){
return comments;
});
return [user.id, user.friendsList, comments];
}).spread(function(userId, friendsList, comments){
// Promises are awesome!
}).catch(function(err){
// An error occurred
})
Waterline-docs is also helpful for reference: https://github.com/balderdashy/waterline-docs