If I have a function call callFunc(), and inside that function there is an async call (like a $http in Angular)... from where I call callFunc() is there some way for me to know when the function - including all of the async within it - has finished executing?
Async function won't technically returns an indicator for "Completed" before it completes. If you have the control for the service controller, you can send to client side a response size first and for each data chunk you send to the client side, you can make your own progress bar to see when it will complete. But other than calling the a callback function, there is no easy way to see it is finished or not.
Seriously, what is your use case?
You can use promise in angular.
function callFunc() {
var deferred = $q.defer();
$http({
method: 'GET',
url: '/someUrl'
}).then(function successCallback(response) {
deferred.resolve('success!');
}, function errorCallback(response) {
deferred.reject('failed.');
});
return deferred.promise;
}
var promise = callFunc();
promise.then(function(res) {
alert('successCallback: ' + res);
}, function(res) {
alert('errorCallback: ' + res);
}, function(res) {
alert('notifyCallback: ' + res);
});
Depending on what you want to do, and the asynch function invovled
calling an asynch function that takes an optional callback
function callFunc(callback) {
// this ones easy, provide a callback or not, doesn't matter
asynchFunction(callback);
}
calling an asynch function that requires a callback function
function callFunc(callback) {
// provide a dummy callback if one isn't provided
callback = callback || function() {};
asynchFunction(callback);
}
calling an asynch function and manioulating the results before calling the optionally provided callback
function callFunc(callback) {
function localCallback(param1, param2) {
var manipulateParams = param1 + param2;
if(typeof callback == 'function') {
callback(manipulateParams );
}
}
asynchFunction(localCallback);
}
alternative: using promises
in the case of $http
function callFunc() {
return $http({
method: 'GET',
url: 'url'
});
}
In the case of a function that doesn't return a promise, lets say it uses the "node" convention
function callFunc() {
var deferred = $q.defer();
somefunc(param, function(err, result) {
if(err) {
deferred.reject(err);
}
else {
deferred.resolve(result);
}
});
return deferred.promise;
}
In both cases, you can use callFunc like so
callFunc().then(function(res) {
console.log('success', res);
}, function(res) {
console.log('error', res);
});
Without specific details from you, that's the best I can offer
Here is a javascript code example of function with a callback.
function callFunc( param1, param2, callback ) {
//your codes of the function
//call the callback with some parameters
window[callback]( param1, param2 );
}
Example of calling the function
callFunc( 'this is param1', 'this is param2', 'call_this_function' );
Example of callback function mentioned in the function call
function call_this_function( param1, param2 ) {
//do something
}
Related
Within my code I have a function that depends on the result of an async call to an API endpoint. In order for the function to execute properly it needs to wait for the result of the call. So I've read up on async calls and from another Stack Overflow question I read that you should make use of callback functions to enable correct execution.
The code below is my attempt to make use of callbacks to allow my function to run successfully, but it doesn't work and at the moment I think the calls are messed up.
I'm not sure how I need to structure this code, but I first need the getInstructionType() call to return its value, then the GetValidationResult() call to return its value, and then the setValidationRowColor() function needs to execute.
getInstructionType(applicationNumber, function(result) {
getInstructionValidationResult(applicationNumber, function(type) {
setValidationRowColor(result);
});
});
function getInstructionValidationResult(applicationNumber) {
var url = //-snip-;
$.get(url, function(data) {
return data;
});
}
function getInstructionType(applicationNumber) {
var url = //-snip-;
$.get(url, function(data) {
return data;
});
}
You could add arguments to the functions which you can use as callbacks. Then you can call those when the AJAX request completes, something like this:
getInstructionType(applicationNumber, function(result) {
getInstructionValidationResult(applicationNumber, function(type) {
setValidationRowColor(result);
});
});
function getInstructionValidationResult(applicationNumber, callback) {
$.get(/*-snip-*/, function(data) {
// some custom logic to work with the response here...
callback && callback(data);
});
}
function getInstructionType(applicationNumber, callback) {
$.get(/*-snip-*/, function(data) {
// some custom logic to work with the response here...
callback && callback(data);
});
}
The alternative to callbacks (which are completely valid) are promises - which is really just another form or callbacks. Assuming you are using jQuery's $.get, you are already making use of Promises:
getInstructionType(applicationNumber, function(result) {
return getInstructionValidationResult(applicationNumber)
.then(function() {
setValidationRowColor(result)
})
});
function getInstructionValidationResult(applicationNumber) {
var url = //-snip-;
return $.get(url)
}
function getInstructionType(applicationNumber) {
var url = //-snip-;
return $.get(url)
}
Note that all I did was return the $.get and added a .then which accepts your callback inside getInstructionType
I am trying to wrap my post/get/put/delete calls so that any time they are called, if they fail they will check for expired token, and try again if that is the reason for failure, otherwise just resolve the response/error. Trying to avoid duplicating code four times, but I'm unsure how to resolve from a non-anonymous callback.
factory.post = function (url, data, config) {
var deferred = $q.defer();
$http.post(url, data, config).then(factory.success, factory.fail);
return deferred.promise;
}
factory.success = function (rsp) {
if (rsp) {
//how to resolve parent's promise from from here
}
}
Alternative is to duplicate this 4 times:
.then(function (rsp) {
factory.success(rsp, deferred);
}, function (err) {
factory.fail(err, deferred);
});
One solution might be using bind function.
function sum(a){
return a + this.b;
}
function callFn(cb){
return cb(1);
}
function wrapper(b){
var extra = {b: b};
return callFn(sum.bind(extra));
}
console.log(wrapper(5));
console.log(wrapper(-5));
console.log(wrapper(50));
For your solution check bellow example
factory.post = function (url, data, config) {
var deferred = $q.defer();
$http.post(url, data, config).then(factory.success.bind({deferred: deferred}), factory.fail.bind({deferred: deferred}));
return deferred.promise;
}
factory.success = function (rsp) {
if (rsp) {
this.deferred.resolve(rsp);
//how to resolve parent's promise from from here
}else {
//retry or reject here
}
}
From what I understand, you just want to resolve the deferred object on success and retry on error in case of expired token. Also you probably want to keep a count of number of retries. If so,
Edit - Seems I misunderstood the question. The answer suggested by Atiq should work, or if you are using any functional JS libraries like underscore or Ramdajs, you could use curry function. Using curry function, you can pass some parameters to the function and the function will get executed only after all the parameters are passed. I have modified the code snippet to use curry function from underscorejs.
factory.post = function (url, data, config) {
var deferred = $q.defer();
$http.post(url, data,
config).then(_.curry(factory.success(deferred)),
_.curry(factory.fail(deferred));
return deferred.promise;
}
factory.success = function (deferred, rsp) {
if (rsp) {
//handle resp
deferred.resolve(rsp);
}
}
factory.fail = function(deferred, err){
//handle retry
deferred.reject(err);
}
I'm probably missing the point somewhere here so I'm looking for advice.
I have a nodejs server which is listening for client connections and, based on the data received, makes calls to an API.
The very first call to that API gets an ID which needs to be used on subsequent calls to group them together.
Where I'm struggling is that the call to the API is necessarily asynchronous and in the callback I'm assigning the ID to a variable. While that async call is being processed by the API server, more data is coming in from the client and needs more API calls made BUT I can't fire them until I know the results from the first call as the second calls depend on it.
What's the proper way to handle this? I feel like I should be using Q to promise the results of the first API call to the second, but I'm not sure how it should be structured. Or should I just be queueing up the API calls until the first completes? How would I do that?
Example problem code :
var server = net.createServer();
//set up the callback handler
server.on('connection', handleConnection);
handleConnection(conn) {
//do some stuff...
firstAPICall();
conn.on('data', handleData);
}
handleData(data) {
//do some stuff...
otherAPIcall();
}
firstAPICall() {
client.get("http://myAPI/getID", function (data, response) {
conn.myID = data[0].myID;
}
}
}
otherAPICall() {
//How do I make sure I actually have a value
//in conn.myID from the first function???
client.post("http://myAPI/storeData", { data: {myID:conn.myID, data:someData} }, function (data, response) {
//do some stuff...
}
}
}
Yes, you should be using promises for this. Make a promise for the id that is asynchronously resolved from the first call, and then use it in the subsequent calls:
handleConnection(conn) {
//do some stuff...
var idPromise = firstAPICall();
conn.on('data', function handleData(data) {
//do some stuff...
otherAPIcall(idPromise).then(function(result) {
…
});
});
}
firstAPICall() {
return Q.Promise(function(resolve, reject) {
client.get("http://myAPI/getID", function (data, response) {
resolve(data[0].myID);
});
});
}
otherAPICall(idPromise) {
return idPromise.then(function(myID) {
return new Promise(function(resolve, reject) {
client.post("http://myAPI/storeData", {
data: {myID:myID, data:someData}
}, function (data, response) {
//do some stuff...
resolve(…);
});
});
});
}
Probably you should factor out creating a promise for the result of a client.get call in an extra function. Also make sure to handle errors correctly there and call reject with them. If client would use the node callback conventions, Q even has some nice helper functions for that.
Try using promises. Then use 'then' to call the otherAPICall()
I think you can assume they will be sending data immediately after connecting. So you can simplify and just check in otherAPICall if you have an ID, if not, you can just use a callback. Promises or the async/await keywords might make things sort of nicer down the line but aren't required for this.
var server = net.createServer();
//set up the callback handler
server.on('connection', handleConnection);
handleConnection(conn) {
conn.on('data', handleData(connm, data));
}
handleData(conn, data) {
//do some stuff...
otherAPIcall(conn);
}
checkID(conn, cb) {
if (!conn.myID) {
client.get("http://myAPI/getID", function (data, response) {
conn.myID = data[0].myID;
cb();
});
} else {
cb();
}
}
otherAPICall(conn) {
checkID(conn, function() {
client.post("http://myAPI/storeData", { data: {myID:conn.myID, data:someData} }, function (data, response) {
//do some stuff...
});
});
}
promises can chain values and are always resolved after the callback occurs with the returned value,
function async(value) {
var deferred = $q.defer();
var asyncCalculation = value / 2;
deferred.resolve(asyncCalculation);
return deferred.promise;
}
var promise = async(8)
.then(function(x) {
return x+1;
})
.then(function(x) {
return x*2;
})
.then(function(x) {
return x-1;
});
promise.then(function(x) {
console.log(x);
});
This value passes through all the success callbacks and so the value 9 is logged ((8 / 2 + 1) * 2 - 1).
I'm trying to do a little web in JavaScript + Ajax and I want to do it recursively. I've never used ajax before and the problem is I don't know to finish functions. The code looks like that:
var cont = 0;
var function1 = function (query) {
$.ajax({
url: '...',
data: {
.
.
.
},
success: function (response) {
instructions;
function2(param1, param2);
}
});
};
var function2 = function (query, param2) {
$.ajax({
url: '...',
data: {
.
.
.
},
success: function (response) {
instructions;
function3(param1, param2, param3);
}
});
};
var function3 = function (query, param2, param3) {
if (cont == 2) {
console.log("finish");
return;
}
var test = $.ajax({
url: '...',
data: {
.
.
.
},
success: function (response) {
if (...) {
cont++;
instructions;
var audio = new Audio(...);
audio.play();
audio.onended = function () {
instructions;
function3(query, param2, param3);
return;
};
} else {
instructions;
function3(query, param2, param3);
};
return;
}
});
return;
};
document.getElementById('search-form').addEventListener('submit', function (e) {
e.preventDefault();
function1(document.getElementById('query').value);
}, false);
So basically, when cont == 2I try to get out of javascript function3 with return; but some part of the program ( I don't know if the success: function (response) or the full javascript function3 ) is still running and instructions are being executed.
How could I solve this?
First off, the way to do this properly is to make use of jQuery's deferred objects.
As you have probably noticed, the program doesn't simply wait at the ajax request, and then proceed to the 'success' handler. This is because Javascript uses a non-blocking/waiting model. So you call $.ajax({params,...}), this sends the request, but whatever's after this will then immediately run, without waiting. Then, once the top level function has finished executing and nothing else is running, the response can be processed, and the 'success' handler is invoked.
So how to do this stuff properly? Start by arranging your request functions like this:
function doRequest1() {
return $.ajax({
url: '...',
data: {
.
.
.
}
});
}
function doRequest2(parameter) {
return $.ajax({
url: '...',
data: {
.
p: parameter
.
}
});
}
Notice that we aren't providing a success handler, but we are returning the value that $.ajax returns. This is a deferred object which is used to represent a request which has been sent, but for which a response hasn't been received/handled. You can attach a handler to the object like this:
var r1 = doRequest1();
r1.then(function() {
// Do stuff on success...
});
A nice thing about these objects is that they can be chained using 'then'.
'then' accepts a function which takes the value of the old request and produces a new request to do next:
var allRequests = doRequest1().then(function(result1) {
return doRequest2("hello");
});
The 'allRequests' variable is now a deferred object representing the result of doRequest2. How do you get this result? You use 'then()', just like any other deferred:
allRequests.then(function(result) {
alert("All requests completed. Result of last one: " + result);
});
Make sure that you understand how the result from 1 request can be used to set the parameters for the next one, or even decide which request to make next.
If you don't need one request's result to determine the next, rather, you just want to run a number of requests and wait for them all to complete, you can use a shortcut, 'when':
$.when(doRequest1(),doRequest2(), doRequest3()).then(function(result1,result2,result3) {
// All done
});
Another nice thing about deferreds is that they can be cancelled:
allRequests.abort();
Using the above, hopefully you can see how to restructure your code so you get a sequence of requests with a function to run after all 3 have completed.
Watch the value of your global variable cont through the flow of your program. It may be that it is (never) equal to 2 when function3() is called and that is why your program continues.
I have make some functions to retrieve data using the Github API. I have the callbacks in place to get the data but I am sure how to understand where a function exits and when I stops modifying things.
For example in the code below, in the first function, when the AJAX call is successful, the callback is executed in the second function where the data is manipulated. Does that mean the the return in the first function is not needed or used? And in the second function is the data used and pushed to the array and then the array returned or is it the other way around where the (empty) array is returned and then the callback does its thing.
I am ultimately trying to get the data from the callback into an object and return that filled object from the parent function.
function makeAJAXCall(hash, cb) {
var returnedJSON, cb = cb, hash = hash;
$.ajax({
accepts: 'application/vnd.github-blob.raw',
dataType: 'jsonp',
url: hash,
success: function (json) {
console.info(json);
returnedJSON = json;
// Time for callback to be executed
if (cb) {
cb(json);
}
},
error: function (error) {
console.error(error);
// an error happened, check it out.
throw error;
}
});
return returnedJSON;
}
function parseBlob(hash) {
var objectedJSON, objectList = [], i;
objectedJSON = makeAJAXCall(hash, function (objectedJSON) { // no loop as only one entry
objectList.push(objectedJSON.content);
});
return objectList;
}
function walkTree(hash) {
var objectedJSON, objectList = [], i, entry;
var hash = 'https://api.github.com/repos/myAccountName/repo/git/trees/' + hash;
objectedJSON = makeAJAXCall(hash, function (objectedJSON) {
for (i = 0; i < objectedJSON.data.tree.length; i += 1) {
entry = objectedJSON.data.tree[i];
console.debug(entry);
if (entry.type === 'blob') {
if (entry.path.slice(-4) === '.svg') { // we only want the svg images not the ignore file and README etc
console.info(entry.path)
objectList.push(parseBlob(entry.url));
}
} else if (entry.type === 'tree') {
objectList.push(walkTree(entry.sha));
}
}
});
console.info(objectList);
return objectList;
}
$(document).ready(function () {
var objects = walkTree('master', function () { // master to start at the top and work our way down
console.info(objects);
});
});
Here you are making an AJAX call A refers to asynchronous, ie your success/error callback will be executed asynchronously.
makeAJAXCall will return before executing success/error of $ajax.
so the objectedJSON = makeAJAXCall will return you undefined
function makeAJAXCall(hash, cb) {
$.ajax({
accepts: 'application/vnd.github-blob.raw',
dataType: 'jsonp',
url: hash,
success: function (json) {
// this function will be executed after getting response from server
//ie Asynchronously
//here cb passed from the makeAjaxCall exist in the closure scope
if (cb) {
cb(json);
}
},
error: function (error) {
console.error(error);
// an error happened, check it out.
throw error;
}
});
}
Now when you call makeAjaxCall the callback function you are passing will exist in the closure scope of $.ajax and will be executed on success of server response
makeAJAXCall(hash, function (objectedJSON) {
//objectJSON contains the response from server
// do all your operations using server response over here or assign it to a global variable
});
check below links
https://developer.mozilla.org/en/JavaScript/Guide/Closures
https://mikewest.org/2009/05/asynchronous-execution-javascript-and-you
or you can make your ajax call in sync using async:false which is highly not recommended
function makeAJAXCall(hash, cb) {
var returnedJSON;
$.ajax({
accepts: 'application/vnd.github-blob.raw',
dataType: 'json',
async : false, //this will make it in sync
url: hash,
success: function (json) {
console.info(json);
returnedJSON = json;
//now makeAJAXCall will wait for success to complete and it will return only after executing success/error
// Time for callback to be executed
if (cb) {
cb(json);
}
},
error: function (error) {
console.error(error);
// an error happened, check it out.
throw error;
}
});
//will wait for success/error before returning
return returnedJSON;
}
In the above case your code will work
function makeAJAXCall(hash, cb) {
var returnedJSON, cb = cb, hash = hash;
return $.ajax({
accepts: 'application/vnd.github-blob.raw',
dataType: 'jsonp',
url: hash,
success: function (json) {
console.info(json);
returnedJSON = json;
// Time for callback to be executed
if (cb) {
cb(json);
}
},
error: function (error) {
console.error(error);
// an error happened, check it out.
throw error;
}
});
}
function parseBlob(hash) {
var objectedJSON, objectList = [], i;
objectedJSON = makeAJAXCall(hash, function (objectedJSON) { // no loop as only one entry
objectList.push(objectedJSON.content);
});
return objectList;
}
function walkTree(hash) {
var objectedJSON, objectList = [], i, entry;
var hash = 'https://api.github.com/repos/myAccountName/repo/git/trees/' + hash;
objectedJSON = $.when(maxAJAXCall)
.then(function(){
//Write the callback
});
Use $.when().then() to call ajax and manage the callbacks better.
.When