Kyle Simpson has an amazing class on pluralsight.
In one of the modules, he goes through a snippet of code that can be safely called asynchronously, and be certain that the results are going to be shown to the user in the same sequence with which the code was executed.
The function in its glory:
function getFile(file) {
var text, fn;
fakeAjax(file, function(response){
if (fn) fn(response);
else text = response;
});
return function(cb) {
if (text) cb(text);
else fn = cb;
}
}
We can call it like so:
I'm having a tough time understanding the getFile function:
where is cb defined? how does it get called, i.e. cb(text) if it's not defined anywhere?
when we call getFile, how does the response get a value at all?
getFile returns an anonymous function:
return function(cb) {
if (text) cb(text);
else fn = cb;
}
so var th1 = getFile("file") ends up assigning that anonymous function to the value of th1, so th1 can now be called with an argument - which becomes cb. So when later, we call th1 with:
th1(function(text1) {
...
we are passing in a second anonymous function (with argument text1) which is assigned to cb (shorthand for 'callback').
The reason it works is that when the ajax is complete, it does one of two things:
if fn is defined, calls fn with the response
if not, it stores the response
Conversely, when the returned anonymous function is called, it does one of two things:
if text is defined (i.e. a result is already received) then it calls the callback with the response
if not, it assigns the callback (cb) to fn
This way, whichever happens first - ajax complete, or thunk called, the state is preserved, and then whichever happens second, the outcome is executed.
In this way, the 'thunks' can be chained to ensure that while the ajax functions happen in parallel the output methods are only called in the sequence in which the fn values are assigned.
I think part of the confusion is unclear variable naming, and the use of liberal anonymous functions with out giving them an intention revealing name. The following should be functionally equivalent with clearer naming (I think):
function getFile(file) {
var _response, _callback;
fakeAjax(file, function(response){
if (_callback) _callback(response);
else _response = response;
});
var onComplete = function(callback) {
if (_response) callback(_response);
else _callback = callback;
}
return onComplete;
}
then:
var onFile1Complete = getFile("file1");
var onFile2Complete = getFile("file2");
var onFile3Complete = getFile("file3");
var file3Completed = function(file3Response) {
output("file3Response");
output("Complete!");
}
var file2Completed = function(file2Response) {
output(file2Response);
onfile3Complete(file3Completed)
}
var file1Completed = function(file1Response) {
output(file1Response);
onFile2Complete(file2Completed);
}
onFile1Complete(file1Completed);
Related
I have share variable between javascript function which is asynchronous. One of them is main thread and another is event based. I want to return value when event is completed.
This is the code:
completeExecution = false; // Shared Variable (Global Variable)
indexDBdata = {}; // Shared Variable (Global Variable)
function getPermission(key) {
var permission_data={};
if(exist_in_local) {
indexdbConnection.getRecordByKey('userPermission',permitKey,function(data){
indexDBdata=data; // Before its complete function return value
});
} else {
// make ajax call & its working fine
}
return permission_data;
}
//get Data from IndexedDB
getRecordByKey:function(tableName,key,readRecords){
if(isEmptyOrNull(readRecords)){
console.log("callback function should not be empty");
return;
}
if(isEmptyOrNull(tableName)){
console.log("table name should not be empty");
return;
}
var returnObj={};
var isSuccessfull=false;
if(this.dbObject.objectStoreNames.contains(tableName)){
var transaction=this.dbObject.transaction(tableName);
var objectStore = transaction.objectStore(tableName);
objectStore.get(key).onsuccess = function(event) {
returnObj=event.target.result;
};
**//Return object after this events compelte**
transaction.oncomplete = function(evt) {
completeExecution=true;
indexDBdata=returnObj;
readRecords(returnObj);
};
transaction.onerror = function(evt) {
completeExecution=true;
indexDBdata={status:'404'};
readRecords("Table Not found");
};
} else {
completeExecution=true;
indexDBdata={status:'404'};
readRecords("Table Not found");
}
}
Problem is while retrieving data from indexedDB it always returns {} (empty object). I want to synchronised event thread and main thread or wait for event to be completed. I don't want to directly manipulate DOM on callbacks I have to return value.
If you have solution to above problem or any other trick then please help me.
Thanks in advance.
I don't find the question very clear, but if I understand it, then you need to learn more about writing asynchronous javascript. In general, functions that call callback functions are void (they return an undefined value). If you want to use the results of two callback functions together, then you will want to chain them so that upon the completion of the first function, which calls its callback function, the callback function then calls the second function which then calls the second callback. So there are four function calls involved. You will want to place the processing logic within the context of the successive callback function, instead of continuing the logic outside of the function and trying to use its return value.
In other words, instead of trying to do this:
function a() {}
function b() {}
var aresult = a();
var bresult = b(aresult);
// processing of both a and b
You would want to try and do something like following:
function a(acallback) {
acallback(...);
}
function b(bcallback) {
bcallback(...);
}
a(function(...) {
b(function(...) {
// all processing of both a and b
});
});
I want to understand one thing about async module in node.js.
I have created a function that map an object from a form to a model object and return this object.
This object is a video with an array of tags.
My question is where can I return the video ? I know normally it is inside the async callback function but if I do that, the object returned is undefined.
Whereas If i return the video object at the end of the whole function, it works but it's not safe as I'm not sure, my async is finished...
By the way, I don't understand the callback function passed in argument to async.each and
called after video.products.push(tag); . What does this function do?
Regards
in my mapping.js :
exports.video = function(object) {
var video = new Video();
video.name = object.name;
video.products = [];
async.each(object.tags, function(tago, callback) {
tag = {
"name" : tago.name
}
video.products.push(tag);
callback();
} ,
function(err) {
if( err ) {
console.log('Error' + error);
throw err;
}
logger.debug("into async" + video);
}
);
logger.debug("end function " );
**//return video;**
}
in my video.js :
var video = mapping.video(object);
logger.debug(video); // return undefined
The simple answer is that you can't - at least not via easy or obvious approach. As its name suggests, async is a library for queuing up asynchronous function calls into the event loop. So your exports.video function simply kicks off a bunch of asynchronous functions, which execute one after the other on an unpredictable time-frame, and then returns immediately. No matter where you try to return your video object within the scope of your function calls which are instantiated by async, the exports.video function will already have returned.
In this case it doesn't really seem like you need asynchronous function calls for what you're doing. I'd suggest that you replace your use of async with something like Underscore's each method, which executes synchronously, instead.
http://documentcloud.github.io/underscore/#each
You'd need to define a callback for your exports.video function e.g..
exports.video = function(object, callback) {
// video code (snip)...
async.each(object.tags,
function eachTag(tag, done) {
// code run for each tag object (snip)...
done();
},
function finished(err) {
// code run at the end (snip)...
callback(thingThatsReturned);
});
};
...and call it like this:
var videoUtils = require('videoUtils');
var tags = getTags();
videoUtils.video({ tags: tags }, function(thingThatsReturned) {
// do something with 'thingThatsReturned'
});
By the way, I don't understand the callback function passed in
argument to async.each and called after video.products.push(tag); .
What does this function do?
The async.each function will call the 'eachTag' function above (2nd argument) for each item in your array. But because it's done asynchronously, and you might do something else async in the function (hit a database/api etc.), it needs to know when that function for that particular array item has finished. Calling done() tells async.each that the function has finished processing. Once all the functions are finished processing (they've all called done()), async.each will run the 'finished' function above (3rd argument).
This is pretty standard async stuff for Node.js, but it can be tricky to get ones head around it at first. Hang in there :-)
Edit: It looks like your code isn't doing anything asynchronous. If it was, then the above code would be the way to do it, otherwise the following code would work better:
exports.video = function(object) {
// video code (snip)...
if (Array.isArray(object.tags)) {
object.tags.forEach(function eachTag(tag) {
// code run for each tag object (snip)...
});
}
return thingThatsReturned;
};
...and call it...
var videoUtils = require('videoUtils');
var tags = getTags();
var thingThatsReturned = videoUtils.video({ tags: tags });
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'm wondering if there is a way to implement a generic "memoize" functional (as in a function with a function as input and a function as output, as python's decorators) capable of handling also cps-style functions.
for a normal function (as in "the result value comes back by the return, the parameters are only for input!") a memoize function can be as simple as (in javascript)
function memoize(fun) {
var cache = {};
return function () {
var args = Array.prototype.slice.call(arguments);
if (args in cache)
return cache[args];
var ret = fun.apply(this, arguments);
cache[args] = ret;
return ret;
};
}
but a cps-style function cannot be memoized by my simple memoize function, cause I need to evaluate "again" the arguments of type function, knowing also the parameter to pass to them.
For example, given the function
function cps(param, next) {
var ret = param + 1;
// setTimeout for simulate async behaviour
setTimeout(function () {
next(ret);
}, 0);
}
maybe I can find that next is a function, but its signature (well... maybe, but it's tricky), and definitely not the parameters used in the function!
Can someone tell me I'm wrong? :D
I'm interested to be able to memoize an half dozen of cps-style functions and I don't want to mess with the logic inserting a "cache" in every one of them.
I'm new to CPS, but I think you'll have to construct your functions in a particular way.
Your CPS functions have the following structure (generalising from your example):
function cps(param, next) {
var ret = someFunctionOfParam(param);
// setTimeout for simulate async behaviour
setTimeout(function () {
next(ret);
}, 0);
}
So, you could use your standard memoizer, and construct the CPS function as well. Keeping this separate for the sake of it, first the CPS-maker (assumes the last argument for the functions is always the function to pass to):
function cpsMaker(transformFunc) {
return function() {
var args = Array.prototype.slice.call(arguments);
var next = args.pop(); // assume final arg is function to call
var ret = transformFunc.apply(this,args);
// setTimeout for simulate async behaviour
setTimeout(function () {
next(ret);
}, 0);
}
}
And then the memoizer can be used in conjunction with it:
function plusOne(val) {
return val+1;
}
var memoPlusOne = memoize(plusOne);
var cpsMemPlusOne = cpsMaker(memoPlusOne);
cpsMemPlusOne(3,function(n){console.log(n)});
The point is to separate the memoization of the transform from the CPS construction.
Thank you for introducing the idea of memoized CPS; even if this answer is rubbish, it has been an eye-opener for me!
Perhaps this has already been addressed, but couldn't find a suitable answer here. Apologies if it's the case. Here's the issue :
function Aclass(){
this.value1 = "a first value"
this.value2 = pulldata()
function pulldata(){
$.getJSON("data.json",function(data){
return data
})
}
}
var obj = new Aclass()
console.log(obj.value1)
console.log(obj.value2)
Result : "a first value" then "undefined"
The data var isn't available out of the pulldata's scope, and I'm not sure what's the best way around this.
Your pullData() function doesn't actually return anything explicitly, so its return is undefined - which means what you are seeing when you log obj.value2 is correct (though undesired).
Inside pullData() you are calling $.getJSON() and passing it an anonymous function as a callback, and it is that anonymous function that returns data but the return doesn't go anywhere because the ajax call is asynchronous and the callback doesn't occur until later.
You could try this:
function Aclass(classInitialisedCallback){
var self = this;
this.value1 = "a first value";
this.value2 = "not available yet";
$.getJSON("data.json",function(data){
self.value2 = data;
if (typeof classInitialisedCallback === "function")
classInitialisedCallback(self);
});
}
var obj = new Aclass(function(newInstance) {
// can use obj or newInstance to refer to the object
console.log(obj.value1);
console.log(newInstance.value2);
});
That is, set up your Aclass() constructor to take a parameter that is a callback function to be executed when the class is ready, i.e., after the result from $.getJSON() is available.
$.getJSON makes an asynchronous ajax call to your "data.json" URL. The second argument you've given it is the callback, which is called when the ajax call is finished.
Your pulldata function, however, just runs through without waiting for the answer and, thus, returns undefined.
It's not possible to do exactly what you're trying to do with asynchronous calls (and you should never use synchronous calls).
You'll have to rewrite your code to do your stuff in the callback instead. The callback is your "function(data){}" argument.
If the function you called wasn't asynchronous however, this is how the JS scope works and should've solved your problem:
function Aclass() {
var that = this;
this.value2 = 'not set';
var pullData = function() {
someCallToSomethingWithACallback(function() {
that.value2 = 'set';
}
}
}