Here's what I'm trying to do.
I'm currently using node.js and one of the things it allows you to do is:
socket.on("sometopic", function() {
// this is a callback
}
Now let's say I have 10 different topics, each of these with one corresponding handling function that I keep in my "window", as such:
windows.callbacks["topic_a"] = function() {
// code for cb a
}
windows.callbacks["topic_b"] = function() {
// code for cb b
}
windows.callbacks["topic_z"] = function() {
// code for cb z
}
And there's a piece of code I would like to have executed at the end of every callback. An easy way out is to create a function with this code and add a call at the end of each callback but this is far from being elegant.
Can anyone suggest a better solution for this? Is there a best practice that I'm unaware of? I'm fairly green to this kind of functional programming.
// THIS IS AN IDEA
// helper
function teardownWith(fn){
return function (cb){
return function(){
return (cb(), fn());
};
};
}
// prepare a modified function
var endWithDate = teardownWith(function(){
log(Date());
});
// pass our callback into the modified function
// endWithDate() returns the final callback.
window.callbacks["cb_a"] = endWithDate(function(){
// code for cb_a
});
Consider using the jQuery Deferred object, and adding a method to execute 'always'
jQuery Deferred Object Documentation
Related
My variable variablex is undefined, how am I supposed to do it right?
Code:
check_content();
function htmlOutput(html){
console.log("first function works")
}
function check_content(){
var variablex = "content";
var html = "foo"
$.when( htmlOutput(html) ).done(function(variablex) {
console.log(variablex);
})
}
JSFiddle
Updated fiddle
You should remove it from the callback done because it will override the variable :
$.when( htmlOutput(html) ).done(function() {
console.log(variablex);
})
NOTE : As #jfriend00 mentioned in the comment bellow there's no reason to use $.when() in your case.
Hope this helps.
Your idea of what to use $.when() for is just wrong. It has no place in this context and thus it does not do what you apparently are expecting it to do.
$.when() requires one or more promises passed to it. It does not have any magical powers to know when some function in it is done.
Plus your htmlOutput(html) function call doesn't even return anything so I have no idea how you expect $.when( htmlOutput(html) ).done(...) to actually have a value.
This explains why your current code does not do what you seem to be expecting. If you want further help, you will have to describe what you're trying to accomplish and why you're using $.when() with a synchronous function that doesn't return a promise.
You can remove the $.when() entirely and just have this because synchronous functions like htmlOutput() block until complete so there's no reason to use promises or $.when() with them:
function htmlOutput(html){
console.log("first function works")
}
function check_content(){
var variablex = "content";
var html = "foo"
htmlOutput(html);
console.log(variablex);
}
check_content();
I have several async functions with varying numbers of parameters, in each the last param is a callback. I wish to call these in order. For instance.
function getData(url, callback){
}
function parseData(data, callback){
}
By using this:
Function.prototype.then = function(f){
var ff = this;
return function(){ ff.apply(null, [].slice.call(arguments).concat(f)) }
}
it is possible to call these functions like this, and have the output print to console.log.
getData.then(parseData.then(console.log.bind(console)))('/mydata.json');
I've been trying to use this syntax instead, and cannot get the Then function correct. Any ideas?
getData.then(parseData).then(console.log.bind(console))('/mydata.json');
Implementing a function or library that allows you to chain methods like above is a non-trivial task and requires substantial effort. The main problem with the example above is the constant context changing - it is very difficult to manage the state of the call chain without memory leaks (i.e. saving a reference to all chained functions into a module-level variable -> GC will never free the functions from memory).
If you are interested in this kind of programming strategy I highly encourage you to use an existing, established and well-tested library, like Promise or q. I personally recommend the former as it attempts to behave as close as possible to ECMAScript 6's Promise specification.
For educational purposes, I recommend you take a look at how the Promise library works internally - I am quite sure you will learn a lot by inspecting its source code and playing around with it.
Robert Rossmann is right. But I'm willing to answer purely for academic purposes.
Let's simplify your code to:
Function.prototype.then = function (callback){
var inner = this;
return function (arg) { return inner(arg, callback); }
}
and:
function getData(url, callback) {
...
}
Let's analyze the types of each function:
getData is (string, function(argument, ...)) → null.
function(argument, function).then is (function(argument, ...)) → function(argument).
That's the core of the problem. When you do:
getData.then(function (argument) {}) it actually returns a function with the type function(argument). That's why .then can't be called onto it, because .then expects to be called onto a function(argument, function) type.
What you want to do, is wrap the callback function. (In the case of getData.then(parseData).then(f), you want to wrap parseData with f, not the result of getData.then(parseData).
Here's my solution:
Function.prototype.setCallback = function (c) { this.callback = c; }
Function.prototype.getCallback = function () { return this.callback; }
Function.prototype.then = function (f) {
var ff = this;
var outer = function () {
var callback = outer.getCallback();
return ff.apply(null, [].slice.call(arguments).concat(callback));
};
if (this.getCallback() === undefined) {
outer.setCallback(f);
} else {
outer.setCallback(ff.getCallback().then(f));
}
return outer;
}
This looks like an excellent use for the Promise object. Promises improve reusability of callback functions by providing a common interface to asynchronous computation. Instead of having each function accept a callback parameter, Promises allow you to encapsulate the asynchronous part of your function in a Promise object. Then you can use the Promise methods (Promise.all, Promise.prototype.then) to chain your asynchronous operations together. Here's how your example translates:
// Instead of accepting both a url and a callback, you accept just a url. Rather than
// thinking about a Promise as a function that returns data, you can think of it as
// data that hasn't loaded or doesn't exist yet (i.e., promised data).
function getData(url) {
return new Promise(function (resolve, reject) {
// Use resolve as the callback parameter.
});
}
function parseData(data) {
// Does parseData really need to be asynchronous? If not leave out the
// Promise and write this function synchronously.
return new Promise(function (resolve, reject) {
});
}
getData("someurl").then(parseData).then(function (data) {
console.log(data);
});
// or with a synchronous parseData
getData("someurl").then(function (data) {
console.log(parseData(data));
});
Also, I should note that Promises currently don't have excellent browser support. Luckily you're covered since there are plenty of polyfills such as this one that provide much of the same functionality as native Promises.
Edit:
Alternatively, instead of changing the Function.prototype, how about implementing a chain method that takes as input a list of asynchronous functions and a seed value and pipes that seed value through each async function:
function chainAsync(seed, functions, callback) {
if (functions.length === 0) callback(seed);
functions[0](seed, function (value) {
chainAsync(value, functions.slice(1), callback);
});
}
chainAsync("someurl", [getData, parseData], function (data) {
console.log(data);
});
Edit Again:
The solutions presented above are far from robust, if you want a more extensive solution check out something like https://github.com/caolan/async.
I had some thoughts about that problem and created the following code which kinda meets your requirements. Still - I know that this concept is far away from perfect. The reasons are commented in the code and below.
Function.prototype._thenify = {
queue:[],
then:function(nextOne){
// Push the item to the queue
this._thenify.queue.push(nextOne);
return this;
},
handOver:function(){
// hand over the data to the next function, calling it in the same context (so we dont loose the queue)
this._thenify.queue.shift().apply(this, arguments);
return this;
}
}
Function.prototype.then = function(){ return this._thenify.then.apply(this, arguments) };
Function.prototype.handOver = function(){ return this._thenify.handOver.apply(this, arguments) };
function getData(json){
// simulate asyncronous call
setTimeout(function(){ getData.handOver(json, 'params from getData'); }, 10);
// we cant call this.handOver() because a new context is created for every function-call
// That means you have to do it like this or bind the context of from getData to the function itself
// which means every time the function is called you have the same context
}
function parseData(){
// simulate asyncronous call
setTimeout(function(){ parseData.handOver('params from parseData'); }, 10);
// Here we can use this.handOver cause parseData is called in the context of getData
// for clarity-reasons I let it like that
}
getData
.then(function(){ console.log(arguments); this.handOver(); }) // see how we can use this here
.then(parseData)
.then(console.log)('/mydata.json'); // Here we actually starting the chain with the call of the function
// To call the chain in the getData-context (so you can always do this.handOver()) do it like that:
// getData
// .then(function(){ console.log(arguments); this.handOver(); })
// .then(parseData)
// .then(console.log).bind(getData)('/mydata.json');
Problems and Facts:
the complete chain is executed in the context of the first function
you have to use the function itself to call handOver at least with the first Element of the chain
if you create a new chain using the function you already used, it will conflict when it runs to the same time
it is possible to use a function twice in the chain (e.g. getData)
because of the shared conext you can set a property in one function and read it in one of the following functions
At least for the first Problem you could solve it with not calling the next function in the chain in the same context and instead give the queue as parameter to the next function. I will try this approach later. This maybe would solve the conflicts mentioned at point 3, too.
For the other problem you could use the sample Code in the comments
PS: When you run the snipped make sure your console is open to see the output
PPS: Every comment on this approach is welcome!
The problem is that then returns a wrapper for the current function and successive chained calls will wrap it again, instead of wrapping the previous callback. One way to achieve that is to use closures and overwrite then on each call:
Function.prototype.then = function(f){
var ff = this;
function wrapCallback(previousCallback, callback) {
var wrapper = function(){
previousCallback.apply(null, [].slice.call(arguments).concat(callback));
};
ff.then = wrapper.then = function(f) {
callback = wrapCallback(callback, f); //a new chained call, so wrap the callback
return ff;
}
return wrapper;
}
return ff = wrapCallback(this, f); //"replace" the original function with the wrapper and return that
}
/*
* Example
*/
function getData(json, callback){
setTimeout( function() { callback(json) }, 100);
}
function parseData(data, callback){
callback(data, 'Hello');
}
function doSomething(data, text, callback) {
callback(text);
}
function printData(data) {
console.log(data); //should print 'Hello'
}
getData
.then(parseData)
.then(doSomething)
.then(printData)('/mydata.json');
Is such pattern possible in jQuery or javascript?:
$.when(function(){
//I init many plugins here, some of them use ajax etc but I dont really control it
//I only do something like $(div).somePlugin() here
$("div").myPlugin()
}).done(function(){
//and this part I want to be executed when all ajaxes and deferred stuff from when part is done
//however I cannot go to every plugin and add something like deferred.resolve() etc.
});
and myPlugin would have for example
$.fn.myPlugin = function(){
$(this).load(someUrl);
};
(but I cannot change myPlugin as its some external code.)
Basically I've got a lot of stuff happening and a lot of this uses async. functions. I want to execute some function when all this async. stuff is done, but I cannot change plugins code so I can't add .resolve() stuff to it.
Yes, this is basically what .when does!
// changes body html
var fisrtApi = $.get("http://something/foo").then(function(r){ $("body div").html(r); });
// inits some API for usage
var secondApi = somePromiseReturningFucntion();
// sets a plugin on top of a page
var somePlugin = someOtherPromiseReturningFn();
$.when(firstApi,secondApi,somePlugin).done(function(r1, r2, r3){
// all of them ready, results are the arguments
});
It is also pretty straightforward to convert a regular non promise returning API to promises.
For example, let's do $(document).ready(function(){
// returns a promise on the document being ready
function whenDocumentReady(){
var d = $.Deferred();
$(document).ready(function(){ d.resolve(); });
return d.promise();
};
Which would let you do:
$.when($.get("http://yourAPI"), whenDocumentReady()).done(function(apiResult,_){
// access API here, the document is also ready.
});
For example - with jQuery twitter, the library provides a callback for when it's done fetching data. You would promisify it:
function getTweets(username, limit){
var d = $.Deferred();
$.twitter(username, limit , function(res){ d.resolve(res); });
return d.promise();
}
Which would let you do:
$.when(getTweets("someusername"),whenDocumentReady()).done(function(tweets){
// document is ready here _and_ the twitter data is available,
// you can access it in the `tweets` parameter
});
If that is what you are looking for, then yes, it is totally possible
$.when(sync(), async(), ajax()).done(function(s,a1, a2) {
console.log( s + ' + ' + a1 + ' + ' + a2) // outputs sync + async + ajax
})
function sync() {
return 'sync'
}
function async() {
var d = $.Deferred();
setTimeout(function() {
d.resolve('async')
}, 100)
return d;
}
function ajax() {
return $.post('http://jsfiddle.net/echo/html/', { html: 'ajax' })
}
I guess the only way to do it is kind of ugly.
If you cannot use deferreds and resolve method, you have no other choice than listen to changes in the dom or context (plugins usually modify the DOM or create new object in the context).
Then you will have to look for $(myElt).hasClass('<class_created_and_applied_by_my_plugin>') turning from false to true, or stuff like this.
You have to create a deferred for each plugin and wrap the previous test in a setInterval to simulate a listener, and finally resolve the deferred.
This way, you can put all your deferred into a when and be sure they are all resolved before going on.
But this is really really uggly cause you have to personalize the test for each plugin.
And I guess, this will certainly slow down the browser too.
I have many pagemethods on my page. Each methods are used for fetching data from the database. I have ordered them in the following way. but my problem is the lines outside the success methods but inside the main function are working before the pagemethods complete the process
function check_valid()
{
// some code
Pagemethod1
function suc1()
{
//some code
PageMethod2
function suc2()
{
//some code
Page Method3
function suc3()
{
//some code
}
function err3(){}
}
function err2(){}
}
function err1(){}
return true; //this line is working before the pagemethods complete the process
}
I'm not familiar with "PageMethods" but it sounds like each of these functions is asynchronous as it is receiving data from a remote database. Because JavaScript operates within one single thread, asynchronous processes usually have an option to attach a callback function that fires once the process is complete. This allows javascript to do other things such as return true while its waiting for your slow database call, explaining your observation.
Instead, use a callback pattern with your database API:
editDatabase(args*, function() {
//Stuff to do database call is complete
});
Alternatively, your database API might use an event pattern:
var myDb = new DB();
myDb.edit(args*)
myDb.bind('complete', function() {
//Stuff to do database call is complete
});
Or, your database API might use promises, which you can read about here.
These patterns may take some getting used to if you are moving from a synchronous language, but they are essential to JavaScript. You can also find a good guide to the asynchronous aspects of JavaScript here.
Change your code as follows
function check_valid()
{
// some code
Pagemethod1
function suc1()
{
//some code
PageMethod2
function suc2()
{
//some code
Page Method3
function suc3()
{
//some code
return true;
}
function err3(){}
}
function err2(){}
}
function err1(){}
}
Because PageMethods will work one by one.
Using a javascript class may help. Also callbacks are key for ajax.
function check_valid(){
var fn = this; //assign this to a variable for ease of use
var callbacks = []; //array to store all results
var calls = []; //all ajax calls
this.complete = function(callback){
function check(){
if(callbacks.length==calls.length){
callback(callbacks);
}else{
setTimeout(function(){
fn.check()
},50);
}
}
return fn;
}
this.callServ(params){
calls.push(params);
params.success = function(ret){
callbacks.push({data:ret,status:'success'});
}
params.error = function(ret){
callbacks.push({data:ret,status:'success'});
}
$.ajax(params);
return fn;
}
return this;
}
This should allow you to do multiple calls systematically and then use a single callback to handle them all. I use jquery ajax to make the ajax call easier, and pass its same parameters.
So it would look like this to use:
var checkValid = new check_valid();
checkValid.callServ({url:url,data:data});
checkValid.callServ({url:url,data:data});
checkValid.callServ({url:url,data:data});
checkValid.complete(function(data){
//data is an array of all call serv returns.
});
This may be far from what you currently have, the amount of information does not tell me enough of what exactly is being done. This example may be a little advanced, but it can give the illusion of being synchronous, at least as close as javascript can get.
I have found my own solution. After a long research i have come to the solution. that is pagemethods will be slow only because of their arrangement. We have to decide where to call it and after which we have to call. Since I have reordered them, it gave some fault. Now its working good.
Kept on seeing this pattern in code, but couldn't find any reference to it in google or SO, strange. Can someone point me to reference for this.async() function?
var done = this.async();
// ...
$.get(path, function(contents) { // or some other function with callback
// ...
done(JST[path] = tmpl);
})
var done = this.async() and done(blah) is a clever trick to return a value fetched from asynchronous call (e.g. $.get) within a synchronous function.
Let's see an example:
var getText = function() {
return "hello";
};
var text = getText();
It's a pretty straightforward function call so no puzzle here. However, what if you need to fetch the text asynchronously in getText() function?
var getText = function() {
return $.get('<some-url>', function(text) {
return text;
}); // ??????
};
call to getText() doesn't return the text you want to get. It returns jquery's promise object.
So how do we make getText() return the text it gets from $.get() call?
var getText = function() {
var done = this.async();
$.get('<some-url>', function(text) {
done(text);
});
};
var text = getText(); // you get the expected text
Magic, right?
I don't know the inner-working of this.async() call yet. I don't know if there is a library provides that function, but you can see that Backbone.LayoutManager uses this trick https://github.com/tbranyen/backbone.layoutmanager/blob/master/backbone.layoutmanager.js (search for this.async).
Also, Tim Branyen (the author of backbone layoutmanager) briefly talks about it in his video tutorial (http://vimeo.com/32765088 around 14:00 - 15:00). In the video, Tim says Ben Alman came up with that trick. Take a look at this as well https://github.com/cowboy/javascript-sync-async-foreach
I think it's a pretty neat trick to mix async and sync functions.
Cheers,
var done = this.async() is a pattern used in Grunt to help perform asynchronous functions within a Task.
You need to invoke done() or done(returnValues) to tell Grunt the task is complete (after your chain of asynchronous tasks).
Read more about it:
https://gruntjs.com/inside-tasks#inside-all-tasks
It is a way to work around the problem of this escaping inside callback. Without this extra reference the code would look like this:
$.get(path, function(contents) { // or some other function with callback
//Wrong! `this` might no longer point to your object
this.done(JST[path] = tmpl);
})
Unfortunately! this inside response callback is not the same as this outside of it. In fact it can be anything, depending on what $.get (calling the callback using) decides it to be. Most of the people use extra reference named that for the same purpose:
var that = this;
// ...
$.get(path, function(contents) { // or some other function with callback
// ...
that.async(JST[path] = tmpl);
})
This pattern also seems reasonable and readable.
Oh, and if you are curious about this syntax:
done(JST[path] = tmpl)
This is an assignment used as an expression. The value of assignment is the right-hand side, so this code is equivalent to:
JST[path] = tmpl;
done(tmpl);