I'm writing a simple controller for a GET request in node.js that uses promises. The code works, but I'm not 100% sure why. I have a getStoriesHelper function that returns a promise, and after I get the result, it does return res.json(listOfStories);. This works, but I was under the impression that returns only breaks out of a single function scope (so I assume it should only break out of the anonymous function scope), and just wondering why it would break out of the getStories function as well?
//for instance, the return statement only breaks out of the anonymous function and not from dummy()
function dummy() {
_.map([1, 2, 3], function(element) {
return element * 2;
}
}
//node.js controller
exports.getStories = function(req, res) {
var studentId = req.params.studentId;
User.findOne({role: 'student', _id: studentId}).exec()
.then(function(student) {
var isNursery = student.status === 'attending class' && user.bookLevel === 'nursery';
getStoriesHelper(student, isNursery).then(function(listOfStories) {
//shouldn't return only break out of this inner function scope
return res.json(listOfStories)
});
});
};
//dummy async code
function getStoriesHelper() {
var deferred = Q.defer();
setTimeout(function() {
deferred.resolve(123)
}, 3000);
return deferred.promise;
}
Your code works because
findOne and getStoriesHelper are asynchronous and res still is in scope within them.
The statement return res.json(listOfStories) does two things. it writes the response to the output stream as well as returns the value returned by res.json() which is never used, so in fact it won't harm if you do not return, the function is about to return already.
getStories function is already ended the moment you called an async function so don't think that inner return has any effect outside that anonymous function.
Related
I have two functions. First contains jQuery.get().done() and it's return value is inside done() callback function, as showed below. (I have no idea if using jQuery matters here)
I may not be understanding something, but my functions looks similar to those in first w3 example: https://www.w3schools.com/js/js_async.asp
async function A(){
jQuery.get("example.com").done(function(result){
if (result.indexOf("Example")){
console.log("sucess");
return 1;
} else {
console.log("Fail");
return -1;
}
})
}
function B(){
A().then(function(result){
console.log("Res:" + result);
})
}
B();
But the output is:
Res:undefined
// here some time passes for A() to complete.
succes
So I can't understand why .then() doesn't wait for function A() to complete and resolve promise, but instead it acts immediately while result is not defined yet ?
This is much better written using actual promises (which jQuery supports) and await as follows:
async function A() {
const result = await jQuery.get("example.com");
if (result.indexOf("Example") !== -1) {
console.log("sucess");
return 1;
} else {
console.log("Fail");
return -1;
}
}
Notes:
Stop using jQuery's .done(). It's non-standard. Use .then() or await.
Don't mix await with .done() or with .then(). Use one model or the other.
There's no need for the res intermediate variable. When you get rid of the .done(), you can just return the value directly.
Note, .indexOf() does not return a boolean. It returns -1 if not found and an index of the position if found. Best not to treat its return value as a boolean.
This isn't really a jQuery thing in particular. It's just async JS in general. I'm not sure you really need the .done() part at all since you're using promises.
But anyway, you have two basic problems.
You need to return something inside of function A, not inside .done(). So you can return jQuery.get().
In function B, you want to wait for function A to complete before hitting the .then() block. That means function B is also async. (In the simple example below you can omit the async keyword on function A.) You need to declare function B as async and then await the completion of function A.
function A() {
return jQuery.get("example.com").done(function(result) {
if (result.indexOf("Example")) {
console.log("sucess", result);
} else {
console.log("Fail", result);
}
});
}
async function B() {
await A().then(function(result) {
console.log("Res:");
// moved to a separate console log, otherwise dev tools might print
// a lot of [objectObject] instead of the actual result
console.log(result);
})
}
B();
If you want to do other stuff inside of function A and then eventually return the result, you can play around with something more like
async function A() {
const response = await jQuery.get('example.com');
// more stuff…
return response;
}
I fixed it by moving return statement outside from the .done(function(){}) and by adding await before jQuery chain. And instead of returning value inside that callback function, now I only pass there value to variable, which is later returned outside of that callback function.
async function A(){
let res = 0;
await jQuery.get("example.com").done(function(result){ //await was added
if (result.indexOf("Example")){
console.log("sucess");
res = 1; // value is passed to variable, which is returned as result outside this callback function
} else {
console.log("Fail");
res = -1;
}
})
return res; // return statement is outside the callback function
}
function B(){
A().then(function(result){
console.log("Res:" + result);
})
}
B();
I guess the problem was, as #cjl750's answer says, that return statement inside the callback function isn't "seen" as a "true return statement" of that function. So function B() probably saw function A() as a function, which doesn't return anything.
EDIT
I've rewritten the code using $.Deferred() object. It's probably better now. Await is now moved to function B(), it doesn't wait for jQuery chain anymore.
function A(){
var dfd = $.Deferred();
jQuery.get("google.com").then(function(result){
if (result.indexOf("Example")){
console.log("sucess");
dfd.resolve(1);
} else {
console.log("Fail");
dfd.resolve(-1);
}
})
return dfd.promise();
}
async function B(){
await A().then(function(result){
console.log("Res:" + result);
})
}
B();
This is a simple version of what I'm trying to do in my application. I have an if statement which evaluates the result of a function call and then populates an array if the statement comes back as true. AFTER the if statement is completely finished, I want to run some more code such as the console.log as seen below.
I understand that the if's evaluation is taking too long to finish and javascript just continues to the console.log because of its asynchronicity. How do I make the code wait for the if statement to complete?
var tabs = [];
if (isTrue()) {
tabs.push('some string');
}
console.log(tabs[1]);
function isTrue() {
setTimeout(function() {
return true;
}, 500)
}
You can just wrap your code in a Promise and consume the returned values by calling then on it:
var tabs = [];
isTrue().then(res => {
if (res) {
tabs.push('some string');
}
return tabs;
}).then(arr => {
console.log(arr);
});
function isTrue() {
//Just wrap your existing code in a Promise constructor
return new Promise((resolve, reject) => {
setTimeout(() => {
//Pass whatever value you want to consume later to resolve
resolve(true);
}, 500)
});
}
You could pass a callback to the isTrue() function, something like:
function isTrue(_callback) {
setTimeout(function() {
// code here
// Call the callback when done
if (typeof(_callback) === 'function')
_callback(tabs);
});
}
function showTabs(tabs) {
console.log(tabs[1]);
}
isTrue(showTabs);
Ought to work.
Using modern javascript, you can achieve that using promises and async/await:
const isTrue = () => new Promise(resolve => setTimeout(resolve, 500, true));
// you can only use `await` inside an `async` function
async function main() {
// better use `let` instead of `var` since `let` is block scoped,
// see:
// <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let>
let tabs = [];
if (await isTrue()) {
tabs.push('some string');
}
// array's index start by 0, not 1
console.log(tabs[0]);
}
main();
(this code also use arrow functions for isTrue.)
isTrue() returns undefined. The return true inside of the setTimeout callback will return back to the timeout call, not to the isTrue() call. The code executes immeadiately and there is no asynchronity involved (except for that timer that does nothing).
This is my code:
.filter('getUserName', function(User) {
return function(id) {
User.get({ _id: id }, function(user) {
return user.name;
});
};
});
I want the middle function to return user.name. User.get is asynchronous, and so I can only return from the middle function once the inner asynchronous function finishes running.
I know 2 ways to run code once the asynchronous code finishes running: using the success callback, or the success promise. However, both of those create an inner function, hence my problem - I don't know how to run code that
Only runs once the asynchronous function finishes and
Returns from the middle function.
This is a terrible fit for a filter, but just as an intellectual exercise, you could have the filter return some default behavior (i.e. return blank) until data is fetched, and once fetched apply the filter. This would necessitate the filter to be $stateful, which is very wasteful - it will run on every digest cycle.
app.filter("foo", function($timeout){
var cache = {};
function genFoo(input){
$timeout(function(){
cache[input] = input + "foo!";
}, 1000);
}
var filter = function(input){
if (input in cache) return cache[input];
genFoo(input);
return "";
};
filter.$stateful = true;
return filter;
});
Plunker
DO NOT do this as a filter :)
In JavaScript I want to override a function on an object, but still call the original function and return it's value. So I'd normally do something like this:
var render = res.render;
res.render = function() {
doSomethingNew();
return render.apply(this, arguments);
};
However, what if that override contains async callbacks that need to be fired first before calling the original function eg:
var render = res.render;
res.render = function() {
var self = this;
var args = arguments;
var middlewares = getSomeMiddleware();
return callNextMiddleware(middlewares, function() {
return render.apply(self, args);
});
};
function callNextMiddleware(middlewares, callback) {
var middlewareFunc = middlewares.shift();
if (middlewareFunc) {
return middlewareFunc.call(function() {
return callNextMiddleware(middlewares, callback);
});
}
else {
return callback();
}
}
Notice that I'm using a 'return' statement where required. I have one problem though, the 'middlewares' variable is an array of functions, each middleware function looks like this:
function myMiddleware(next) {
performLongRunningAsyncDataAccess(function() {
next();
});
}
Because it doesn't use 'return next()' the return value of the original res.render method is never passed back. I can get this to work if I get all the middleware functions to use 'return next()', but they come from an external source so I have no control over them, I can only guarantee that they will call 'next()'.
A bit of background, this is a Node.js app. The middleware is basically Connect middleware, and I'm trying to override the Express.js res.render method.
Generally it is a bad idea to mix asynchronous functions with return statements. Everything that you want to return, you can pass as arguments to your callback functions. So I still hope I understand your code correctly but I would assume, that you call the render function, which then grabs an array of middleware functions. Then you want to execute all the functions in that array, using the next as an callback to the previous. After all the functions have been executed, the render function should be called again, thus creating kind of an infinite loop. Assuming all of that, lets take a look at some of your return statements:
return middlewareFunc.call(function() {
return callNextMiddleware(middlewares, callback);
});
The first return in this block is useless since middlewareFunc is asynchronous and will therefore most likely return undefined. The second return statement is also useless, since it returns from the function, that you use as callback. But since your callback is just called by using next();, the return value will never be used.
else {
return callback();
}
In this block callback is the render function. So lets take a look at this function:
res.render = function() {
var self = this;
var args = arguments;
var middlewares = getSomeMiddleware();
return callNextMiddleware(middlewares, function() {
return render.apply(self, args);
});
};
So all last three return statements are essentially there, because you want to return something from your render function. But to be consistent, you should consider using a callback for that function as well:
res.render = function(callback) {
var self = this;
var args = arguments;
var middlewares = getSomeMiddleware();
callNextMiddleware(middlewares, function() {
//this will be called after all the middleware function have been executed
callback();
render.apply(self, args);
});
};
So basically you are getting rid of all the return statements and using pure asynchronous design patterns.
callNextMiddleware should return its recursive call's return value, not middlewareFunc's.
if (middlewareFunc) {
var result;
middlewareFunc.call(this, function() {
result = callNextMiddleware(middlewares, callback);
});
return result;
}
Fiddle: http://jsfiddle.net/mWGXs
I've got an app that works in most browsers. Unfortunately, with Internet Explorer 10 on Windows Phone 8, I'm very limited to what I can change. There are a few functions that don't work in WP IE10 - but I want to fix these by hijacking the functions, as opposed to rewriting any code.
var valueNeeded = myFunction("settings")
// This function cannot have another param without me having to change lots of code
myFunction = function (field) {
db.values('tblStorage', null, 10).done(function (results) {
return (results[i].value);
});
}
The problem I have is the database call isn't blocking/synchronous and so the return doesn't work. Normally I'd add a callback into the param, but as I want ONE codebase for all browsers, I can't really do that.
Any thoughts?
Asynchronous functions require the whole callstack to be asynchronous.
I would rewrite your code to this:
myFunction("settings").done(function (value) {
console.log("value", value);
});
myFunction = function (field) {
// v-- added return here v-- then here, so we return another promise (so we can call .done())
return db.values('tblStorage', null, 10).then(function (results) {
return (results[i].value);
});
}
So what does myFunction return? It returns a promise. The promise that, when it is resolved, it will have the value you requested.
To be more exact: myFunction returns the value of db.values(/**/).then(), which is a promise. db.values(/**/) also returns a promise. You can stack promises and chain them together to get a kind of asynchronous pipe-and-filter.
If you unchain the functions you'd get something like this:
myFunction = function (field) {
var value_promise = db.values('tblStorage', null, 10);
var filtered_value_promise = value_promise.then(function (results) {
return (results[i].value);
});
return filtered_value_promise;
}