I am not very familiar with callback() functions. I am looking for an explanation and an example of what a proper use case might be. In my example below, how can I utilize a callback and also, should I?
Here I have two functions:
addShape = function () {
ExampleService.createShape(function () { //ajax
shapeMade = true;
//anything else etc.......
});
}
deleteShape = function () {
ExampleService.removeShape(function () { //ajax
shapeMade = false;
//anything else etc.......
});
}
The third function (focus of my question)
resetShape = function () {
deleteShape();
addShape();
console.log('example');
}
When I call the resetShape() function, the example gets logged to the console before both deleteShape() and addShape() have finished.
Would this be a situation to use a callback()? If so, how? If not, why?
Callbacks are used for asynchronous functions. In this case, since you have multiple async function to wait for, you probably want to use a Promise, and Promise.all to execute the callback when all async operations are complete:
resetShape = function () {
Promise.all([ deleteShape(), addShape() ]).then( function() {
console.log('example');
});
}
Note that, for this to work, your functions "deleteShape" and "addShape" have to return the promise object:
addShape = function () {
return new Promise(function(resolve, reject) {
ExampleService.createShape(function () {
shapeMade = true;
resolve();
});
});
A callback is used mainly for two reasons, to give another object or function a way to tell us something we were waiting for has completed (or changed) or a way to get something from us when they require it.
First case: imagine a function that makes an asynchronous request to
a server. We don't know how much time it'll take to complete, so we
give the requester a way to tell us it's done:
getDataFromServer('foo.php', callback); //callback will be called when the request
//is done. We can do something else meanwhile.
Second case. I could create and object that, when needed, will ask
for more data (ie, getting more rows to append to a table when we
scroll down)
var tableBuilder = new TableBuilder(container, dataGetterCallback);
dataGetterCallback will be called whenever TableBuilder needs more rows. It'll have the logic to give it to them (maybe by receiving a parameter that tells it in which index to start).
Hope this gives you an idea.
Related
I have a super simple question. I have an onclick function that takes a good 10 seconds to complete. Therefore I want to add a 'Loading, please wait' text while the function completes.
If I do this:
javascript:
function onclickFunction(){
addPleaseWait();
runSlowFunction();
addFinishedText();
}
Then of course the end result is that the slow function runs first, then addPleaseWait and FinishedText run 2nd and 3rd. What is the proper way to do this?
If you are allowed to use ES6 function, I would suggest you use promise to run your function.
function onclickFunction() {
startLoader();
longFunction().then(function(data) => {
// long function is done, remove loader
return removeLoader();
})
}
function longFunction() {
return new Promise(function(resolve, reject) {
/*
* the parameter resolve tell when to finish. If you pass a function, it will finish once all the instruction of the function is done.
* the parameter reject is used to manage error.
*/
resolve(function() {
// Do you long function stuff
})
})
}
This method could seams a little bit complicated for what you are trying to achieve, but I think it is a great way to handle slow function. To make sure everythings comes in the right order.
If you wish to learn more about promise, look at this Mozilla docs link
function call() {
//your function here for example :
$("#yourFunction").html("yourFunction is executing....");
$("#test").html("Please Wait....."); // during your function
setTimeout(function() {
$("#yourFunction").html("yourFunction is completed....");
$("#test").html("done!!!!"); // after your function completed
}, 1000); // if you need more or less time change here
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<button onclick=call()>Click</button>
<div id="yourFunction"></div>
<div id="test"></div>
You need a callback in your slow function, for example
runSlowFunction(callback) {
// ... do all slow things, and in the end, call your function
callback();
}
& then, your onclick function changes to
function onclickFunction(){
addPleaseWait();
runSlowFunction(addFinishedText);
// pass addFinishedText as the callback,
// which gets called after the slow function is done
}
What is the best way to wait for a function to complete before the next function is called?
I have a scenario where two functions are called consecutively, one after the other. I need the first one to complete before the second one is called.
Function1();
Function2();
How do I tell the code to wait for Function1 to complete before attempting Function2?
WebDevelopment: solution can be javascript or jQuery, or Ajax based.
Thanks...
You can do many ways but a very simple Example
function first(){
// define every thing here and then at the end call your second function
function2();
}
Checkout more possibilities here
You could either use Promise or callback to solve this problem, with Promises being the more modern and cleaner way of doing things:
Promise:
function foo() {
return new Promise(function(resolve, reject) {
// do some work
resolve(optionalParam);
// something caused an error whoops
reject(optionalParam);
})
}
function bar() {
// do something
};
// Call bar only once foo finishes
foo().then(function() {
bar();
}, function() {
// this is the reject case an error here
});
Promises are able to be chained, meaning that if bar was a Promise we could chain another then event to that.
You could also use callbacks to solve your problem
function foo(cb) {
var data = 'alex';
cb(data);
}
function bar(name) {
// some processing
console.log(name);
}
// Call foo, then execute bar when foo finishes
foo(function(data) {
bar(data); // alex would be logged once the callback fires
});
In the callback example here, we pass a function as a parameter, once the running function block encounters your invocation of the cb parameter, it will invoke the function which emulates the asynchronousy. Just remember though that it does not stop the execution context on that function. If we had code like:
function foo(cb) {
var data = 'alex';
cb(data);
console.log('this is after so don't print it'); <-- still prints
};
The final console.log will print when the callback finishes its time on the event queue (unless of course you fire an even in CB like a http request, in which the log will fire and then the http request would finish after).
To stop the last console log you would need to explicitly tell javascript to return when cb is called to prevent further execution in that method, and personally this is where I favour Promises, once you resolve or reject the promise the execution context within that function scope is stopped.
if your in the browser, just use callback it's the easiest and cross-browser way to do it.
Exemple:
function sayBill() {
console.log('bill');
}
function sayHiAfterTwoSeconds(callback) {
setTimeout(function() {
console.log('hi');
// call the function you passed in parameter
if (typeof callback === 'function') {
callback();
}
});
}
sayHiAfterTwoSeconds(sayBill);
// will output 'bill hi'
https://jsfiddle.net/80bbbjco/
If you want better way to do it, but not cross browser there is promise : https://developer.mozilla.org/en-US/docs/Mozilla/JavaScript_code_modules/Promise.jsm/Promise
Or even more modern but not supported ATM async await: https://jakearchibald.com/2014/es7-async-functions/
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');
I was unsure how node.js was able to realize what functions where async and which were not and how to create a custom async function.
Say I wanted to create a custom asynchronous function. I would be surprised if just because I called my last argument to the async function callback or cb that it would just know its an async function:
function f(arg1, callback){
//do stuff with arg1
arg1.doStuff()
//call callback
callback(null, arg1.result());
}
I tried something like that and it did not work async. How do you tell node.js that f is actually async?
NOTE: this answer was written in 2014, before the existence of async function, and before Promises gaining popularity. While the same principles apply as well, I would recommend reading on Promises before trying to get your head around how they relate to "traditional" callback-driven async functions.
To create a function that calls its callback asynchronously, you have to use some platform-provided async primitive (typically IO-related) on it - timers, reading from the filesystem, making a request etc.
For example, this function takes a callback argument, and calls it 100ms after:
function asyncFn(callback) {
setTimeout(() => {
callback();
}, 100);
}
A possible reason for making a function async when it doesn't need to be, is for API consistency. For example, suppose you have a function that makes a network request, and caches the result for later calls:
var cache = null;
function makeRequest(callback) {
if (!cache) {
makeAjax(result => {
cache = result;
callback(result);
});
} else {
callback(cache);
}
}
The problem is, this function is inconsistent: sometimes it is asynchronous, sometimes it isn't. Suppose you have a consumer like this:
makeRequest(result => doSomethingWithResult(result));
doSomethingElse();
The doSomethingElse function may run before or after the doSomethingWithResult function, depending on whether the result was cached or not. Now, if you use an async primitive on the makeRequest function, such as process.nextTick:
var cache = null;
function makeRequest(callback) {
if(!cache) {
makeAjax(result => {
cache = result;
callback(result);
});
} else {
process.nextTick(() => callback(cache));
}
}
The call is always async, and doSomethingElse always runs before doSomethingWithResult.
Only native functions (with access to the event loop) are asynchronous. You would need to call one of them to get asynchronity for your callback. See What is a simple example of an asynchronous javascript function?.
If you aren't using any, there's hardly a reason to make your function asynchronous.
If I have an ajax call off fetching (with a callback) and then some other code running in the meantime. How can I have a third function that will be called when both of the first 2 are done. I'm sure it is easy with polling (setTimeout and then check some variables) but I'd rather a callback.
Is it possible?
You could just give the same callback to both your AJAX call and your other code running in the meantime, use a variable to track their combined progress, then link them to a callback like below:
// Each time you start a call, increment this by one
var counter = 0;
var callback = function() {
counter--;
if (counter == 0) {
// Execute code you wanted to do once both threads are finished.
}
}
Daniel's solution is the proper one. I took it and added some extra code so you don't have to think too much ;)
function createNotifier() {
var counter = 2;
return function() {
if (--counter == 0) {
// do stuff
}
};
}
var notify = createNotifier();
var later = function() {
var done = false;
// do stuff and set done to true if you're done
if (done) {
notify();
}
};
function doAjaxCall(notify) {
var ajaxCallback = function() {
// Respond to the AJAX callback here
// Notify that the Ajax callback is done
notify();
};
// Here you perform the AJAX call action
}
setInterval(later, 200);
doAjaxCall(notify);
The best approach to this is to take advantage of the fact that functions are first-order objects in JavaScript. Therefore you can assign them to variables and invoke them through the variable, changing the function that the variable refers to as needed.
For example:
function firstCallback() {
// the first thing has happened
// so when the next thing happens, we want to do stuff
callback = secondCallback;
}
function secondCallback() {
// do stuff now both things have happened
}
var callback = firstCallback;
If both your pieces of code now use the variable to call the function:
callback();
then whichever one executes first will call the firstCallback, which changes the variable to point to the secondCallback, and so that will be called by whichever executes second.
However your phrasing of the question implies that this may all be unnecessary, as it sounds like you are making an Ajax request and then continuing processing. As JavaScript interpreters are single-threaded, the Ajax callback will never be executed until the main body of code that made the request has finished executing anyway, even if that is long after the response has been received.
In case that isn't your situation, I've created a working example on my site; view the source to see the code (just before the </body> tag). It makes a request which is delayed by the server for a couple of seconds, then a request which receives an immediate response. The second request's response is handled by one function, and the first request's response is later handled by a different function, as the request that received a response first has changed the callback variable to refer to the second function.
You are talking about a thing called deferred in javascript as #Chris Conway mentioned above. Similarly jQuery also has Deferred since v1.5.
Check these Deferred.when() or deferred.done()
Don't forget to check jQuery doc.
But to give you some idea here is what I am copying from that site.
$.when($.ajax("/page1.php"), $.ajax("/page2.php")).done(function(a1, a2){
/* a1 and a2 are arguments resolved for the
page1 and page2 ajax requests, respectively */
var jqXHR = a1[2]; /* arguments are [ "success", statusText, jqXHR ] */
if ( /Whip It/.test(jqXHR.responseText) ) {
alert("First page has 'Whip It' somewhere.");
}
});
//Using deferred.then()
$.when($.ajax("/page1.php"), $.ajax("/page2.php"))
.then(myFunc, myFailure);
Something like this (schematic):
registerThread() {
counter++;
}
unregisterThread() {
if (--counter == 0) fireEvent('some_user_event');
}
eventHandler_for_some_user_event() {
do_stuff();
}
You can do this easily with Google's Closure library, specifically goog.async.Deferred:
// Deferred is a container for an incomplete computation.
var ajaxFinished = goog.async.Deferred();
// ajaxCall is the asynchronous function we're calling.
ajaxCall( //args...,
function() { // callback
// Process the results...
ajaxFinished.callback(); // Signal completion
}
);
// Do other stuff...
// Wait for the callback completion before proceeding
goog.async.when(ajaxFinished, function() {
// Do the rest of the stuff...
});
You can join multiple asynchronous computations using awaitDeferred, chainDeferred, or goog.async.DeferredList.