How can I add a callback to the .apply() method? - javascript

In my code, I have an array of function calls. I loop over these calls and use .apply() to call them. The problem is that if the call of the new function takes any sort of time, the loop will .apply() and call the next function before the prior function is finished. >.< Here is an example:
function someFunc(element, calls){
if(calls.length){
fn = calls[0];
calls.shift();
fn.apply(element, args);
someFunc(element, calls);
}
}
So if there was a callback on the apply function then this could work how I want it to. i.e.
function someFunc(element, calls){
if(calls.length){
fn = calls[0];
calls.shift();
fn.apply(element, args, function(){
someFunc(element, calls);
});
}
}
I also have a question about calling someFunc inside of a callback function. The functions in my calls array affect my element variable. So I want to make sure after it gets changed that it gets passed to someFunc in the callback so the next function can manipulate it as well. Sometimes I just get confused with the this context. :)
If it helps, I am using jQuery. I know how to add callbacks to jQuery methods but I don't know how to do that when I'm dealing with native JavaScript code. How can I add a callback to the .apply() method?

Make sure that every function you call returns a promise. You can then "wait" for that promise to be "resolved" before continuing with the next function in your list:
function someFunc(element, calls) {
if (calls.length) {
var fn = calls.shift();
fn.apply(element, args).done(function(el) { // what's args?
el = el || element; // default to previous element if necessary
someFunc(el, calls);
});
}
}
with each function looking something like:
function myFunc1(el) {
var def = $.Deferred();
// do something async, and "resolve" the deferred object in the async callback
...(function() {
def.resolve(el); // this "el" will get passed to the next function
});
return def.promise();
}
If the asynchronous task is an AJAX call you can just return the jqXHR result of $.ajax directly instead of creating a new deferred object.

Related

How the callback is executed in this function?

const getData = (cb) => {
setTimeout( () => {
cb({ data: ['there', 'is', 'stuff', 'here'] })
}, 100)
}
getData( data => {
console.log(data);
});
Here is the example of the javascript callback. Could some one let me know how this functions is executed into the javascript callback?
Here what is the function inside getData(cb) ? How it will be executed ? How the functions is passed as callback inside cb and return to the console.log
Regards.
The function inside getData is a callback being passed to setTimeout, which is one way to schedule a call to a function to happen in the future. In this case, it's asking for that callback to happen roughly 100ms later. getData returns before that happens.
The setTimeout callback is a closure¹ over the context where it's created, which means it has access to cb even after getData has returned. So when the browser's timer calls the callback, the callback can call cb. The call to cb uses an object literal to create an object to pass to cb.
In the call to getData, the author is passing a function as cb that logs the data it receives.
So:
getData is called, passing in a function that will log the argument it gets.
getData calls setTimeout to schedule a callback in about 100ms, passing in another function for the timer to call.
getData returns.
About 100ms later, the browser's timer subsystem triggers a call to the callback passed to setTimeout.
That callback creates an object and calls cb, passing the object to it.
That callback (the one passed to getData) logs the data object it receives.
¹ "closure" — see: SO, my anemic blog
In order to understand the code you can just simplify it by naming the anonymous functions. One example could be:
function logData(data) {
console.log(data);
}
const getData = (cb) => {
// `cb` is `logData` function when `getData` is called
function timeoutCallback() {
var data = { data: ['there', 'is', 'stuff', 'here'] };
cb(data);
}
setTimeout(timeoutCallback, 100)
}
getData(logData);
Does that make sense?
1- first global execution context created
2- get data function will be called then it will wait for 10 seconds inside the event loop then it will be come to execution context and printed to console.

How can one wait for a function to complete before the next function is called?

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/

Javascript async function composition

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');

Returning from nested functions

function outer() {
$(['hi', 'there']).each(function(idx, e) {
console.log(e);
return;
});
alert("I don't want to be called");
}
function outer() {
$.get('http://test.com', function (data) {
console.log(data)
return; // I want to terminate the entire outer() function here.
});
alert("I don't want to be called");
}
What's the convention of breaking out of nested functions in cases like this? When using for loops, returning inside them terminates the entire function that encloses them. However, since $.each() is an individual function call, returning from it only ends itself, not the outer function. I could simply return twice, once inside and once outside in that case, but I am not sure how I would deal with $.ajax() because it is necessary to terminate the function only when I get a successful response.
Here is a summary of how it all works.
.each()
return true; // skips to the next iteration of .each()
return false; // exits the .each() loop
In short there's no way of breaking out of the function containing .each() in a single statement.
$.get()
return [true|false]; // makes no sense at all.
As the return in $.get() does not get executed until the ajax call is complete, it wont serve much purpose. Even when you make a synchronous ajax call, a return statement in the success callback still does not do anything substantive. Think of a return from a function as a value to be assigned to the calling statement.
What's making it necessary to break out of your functions?
For $.each(), you can stop the iteration with return false; in the callback, as described in the jQuery documentation.
This won't return from the calling function, but you can set a flag and test it in the outer function.
If you want an easy way to return from the outer function from inside the loop, you're better off with a simple for loop:
var array = [ 'hi', 'there' ];
for( var i = 0; i < array.length; ++i ) {
var e = array[i];
console.log(e);
return;
}
alert("I don't want to be called");
For $.get(), you should add some console.log() calls to your code and observe the order in which they are called:
function outer() {
console.log( 'in outer before $.get is called' );
$.get('http://test.com', function (data) {
console.log( 'inside $.get callback' );
});
console.log( 'in outer after $.get returns' );
}
Now what you'll notice is the order of the log messages:
in outer before $.get is called
in outer after $.get returns
inside $.get callback
See how the callback is the last thing? It's called after the outer function finishes. Therefore, there is nothing this callback can do to prevent the rest of outer from executing.
So you need to think some more about what you need to do and figure out a different way to accomplish it.
If you use $.ajax() instead of the short hand $.get(), it is possible to specify that it be executed synchronously.
$.ajax({
url: 'http://test.com',
success: function (data) {
},
async: false
});
Typically, when you want to make a decision based on the return value of a function you have it return a value and then use a conditional statement to determine what happens next.

Having problem with a callback method for this jQuery getJSON AJAX call

i've got a utility .js file that grabs some data via Ajax. Now this utility method doesn't know who will call it.
So when the Ajax async completes, it needs to send an object back to the caller. I'm not sure how to do this :(
This is the code I have...
function someMethod(a, b, c) {
// ... stuff ...
// Now fire it off, asynchronously!
var request = $.getJSON(url, function (jsonResult) {
var result =
{
json: jsonResult,
contentLength: request.getResponseHeader("Content-Length")
};
// TODO: Now return this result to the caller.
});
}
of course, i can't use return result; because this is async. I feel like i need to pass in a parameter that is the function which the ajax code above needs to call, when the result has been async finished. Just not sure how .. because remember .. this is method has no idea who is calling it .. so it doesn't know about any other methods and stuff.
Any ideas, please?
You are right. You will need to pass in the function to "someMethod" so that you can return the data to it. Depending on what the callback function does you might also need to set it's context properly by using the call or apply methods. The "context" is what what the "this" keyword will be set to in the callback function.
If your callback function doesn't make any references to the keyword "this" then you can ignore the use of call() or apply() and just call the function as in the second example. (e.g. callback(result));
function someMethod(a, b, c, callback, context) {
// ... stuff ...
// Now fire it off, asynchronously!
var request = $.getJSON(url, function (jsonResult) {
var result =
{
json: jsonResult,
contentLength: request.getResponseHeader("Content-Length")
};
// Now return this result to the caller. With the context set
callback.call(context, result);
// Example of callback without setting the context
//callback(result);
});
}

Categories

Resources