I'm using an anonymous function to perform some work on the html I get back using Restler's get function:
var some_function() {
var outer_var;
rest.get(url).on('complete', function(result, response) {
if (result instanceof Error) {
sys.puts('Error: ' + result.message);
} else {
var inner_var;
// do stuff on **result** to build **inner_var**
outer_var = inner_var;
}
});
return outer_var;
}
How can I get the value of inner_var out to the some_function scope and return it? What I have written here doesn't work.
The get call is asynchronous, it will take some time and call your callback later. However, after calling get, your script keeps executing and goes to the next instruction.
So here is what happens:
you call get
you return outer_var (which is still undefined)
... sometimes later ...
get result has arrived and the callback is called.
outer_var is set
You can't have your some_function return a value for something that is asynchronous, so you will have to use a callback instead and let your code call it once data is processed.
var some_function(callback) {
rest.get(url).on('complete', function(result, response) {
if (result instanceof Error) {
sys.puts('Error: ' + result.message);
} else {
var inner_var;
// do stuff on **result** to build **inner_var**
callback(inner_var);
}
});
}
Like most of the modules of node, Restler is also a event based library having async calls. So at the time you do return outer_var; the complete callback was not necessarily called (With some libraries it could be if it the result was cached, but you should always expect that it is called asynchronous).
You can see this behavior if you add some logging:
var some_function() {
var outer_var;
console.log("before registration of callback"); //<----------
rest.get(url).on('complete', function(result, response) {
console.log("callback is called"); //<----------
if (result instanceof Error) {
sys.puts('Error: ' + result.message);
} else {
var inner_var;
// do stuff on **result** to build **inner_var**
outer_var = inner_var;
}
});
console.log("after registration of callback"); //<----------
return outer_var;
}
So if you would like to do something with this value you would need to call the function that should do something with this value out of your complete callback.
Remove the var outer_var; from your function some_function;
Declare the var outer_var; upper then your function
It should be work after you did step 1, not really need step 2.
Related
I've been reading about async functions in JavaScript and found out that I don't quite understand the following piece of code that comes from here.
Here it is:
doSomething(function(result) {
doSomethingElse(result, function(newResult) {
doThirdThing(newResult, function(finalResult) {
console.log('Got the final result: ' + finalResult);
}, failureCallback);
}, failureCallback);
}, failureCallback);
What I don't understand is where all these results come from.
They are callbacks. A callback is just simply a function which is passed as an argument to another function and is used to do something with the result of some operation done by the function which receives the callback as an argument. You can write your own. A simple example:
function callMe(callback) {
let addition = 2 + 2;
callback(addition);
}
callMe(function(result) {
console.log(result);
});
callMe calls its callback function with the result of 2 + 2. You then receive that result inside the callback function when you use callMe, and you can then write your own custom code to do whatever you want with it.
The beauty of JavaScript is that you already have all you need to test it.
The code you posted references 4 functions (don't forget the failure callback):
doSomething(function(result) {
doSomethingElse(result, function(newResult) {
doThirdThing(newResult, function(finalResult) {
console.log('Got the final result: ' + finalResult);
}, failureCallback);
}, failureCallback);
}, failureCallback);
Those functions are not written. So let's write them:
var failureCallback = function() {
console.log('something went wrong');
}
var doSomething = function(callback) {
window.setTimeout(function() {
var result = Date.now(); // Create some data that will change every time the function is invoked
callback(result);
}, 500);
}
var doSomethingElse = function(res, callback) {
window.setTimeout(function() {
var result = [res]; // all this function really does is put the first argument in an array
callback(result);
}, 500);
}
function doThirdThing(res, callback) {
window.setTimeout(function() {
res.push(Math.PI); // res is supposed to be an array, so we can add stuff to it.
var result = res;
callback(result); // here we could have used res as a value, but I kept it consistent
}, 500);
}
doSomething(function(result) {
doSomethingElse(result, function(newResult) {
doThirdThing(newResult, function(finalResult) {
console.log('Got the final result: ', finalResult); // switched to a comma to display properly in the console
}, failureCallback);
}, failureCallback);
}, failureCallback);
To create some asynchronicity I used setTimeout so overall you have to wait 1.5 seconds.
The 3 functions that use a callback function as an argument, simply execute the function they were given. This is absolutely standard in JavaScript where functions are first class objects: you can pass them around as arguments like any other value type.
Javascript is a single threaded language, callbacks are used as a way to control the flow of execution when a asynchronous block of code ends in a non-blocking way. A callback is normally just another function (function B) that is passed into the asynchronous function (function A) to run when function A completes. I.E
doSomething(func) {
// do async code.
// call func
// func()
}
you haven't posted the inner blocks of those functions but i think we can all safely assume "result" is the response from the server passed back into the callback function I.E
doSomething(callback) {
//fetch or ajax or whatever to server store response.
//pass response BACK to callback function
callback(response)
}
Lets imagine that I have some code:
var someString = "";
function do1(){
doA();
doB();
}
function doA(){
// some process that takes time and gets me a value
someString = // the value I got in prior line
function doB(){
//do something with someString;
}
What is the correct way to make sure somestring is defined by doB tries to use it? I think this is a situation that calls for a callback, but I'm not sure how to set it up?
Usually, I have solved this problem like following code by callback parameter. However, I don't know this is correct answer. In my case, it's done well.
var someString = "";
function do1(){
doA(doB);
}
function doA(callback){
// some process that takes time and gets me a value
someString = // the value I got in prior line
callback();
}
function doB(){
//do something with someString;
}
I usually write these such that the function can be called with, or without, the callback function. You can do this by calling the callback function only if typeof callback === 'function'. This allows the function which includes the possibility of a callback to be a bit more general purpose. The call to the callback(), obviously, needs to be from within the callback of whatever asynchronous operation you are performing. In the example below, setTimeout is used as the asynchronous action.
var someString = "";
function do1() {
doA(doB); //Call doA with doB as a callback.
}
function doA(callback) {
setTimeout(function() {
//setTimeout is an example of some asynchronous process that takes time
//Change someString to a value which we "received" in this asynchronous call.
someString = 'Timeout expired';
//Do other processing that is normal for doA here.
//Call the callback function, if one was passed to this function
if (typeof callback === 'function') {
callback();
}
}, 2000);
}
function doB() {
//do something with someString;
console.log(someString);
}
do1();
You can, of course, do this without using a global variable:
function do1() {
doA(doB); //Call doA with doB as a callback.
}
function doA(callback) {
setTimeout(function() {
//setTimeout is an example of some asynchronous process that takes time
//Simulate a result
var result = 'Timeout expired';
//Do other processing that is normal for doA here.
//Call the callback function, if one was passed to this function
if (typeof callback === 'function') {
callback(result);
}
}, 2000);
}
function doB(result) {
console.log(result);
}
do1();
function someFunctionA(callback){
var someString = "modify me";
callback(someString);
}
function someFunctionB(someString){
// do something
}
function main() {
someFunctionA(somefunctionB);
}
I have 3 functions in my javascript code. I want to call the 3rd function as soon as both function1 and function2 are over executing.
func_one();
func_two();
func_three(); // To be called as soon both of the above functions finish executing.
Note that they may take variable time since function 1 and 2 are for fetching geolocation and some ajax request respectively.
How about this?
func_one() {
// Do something
func_two();
}
func_two() {
// Do something
func_three();
}
func_three() {
// Do something
}
There are two ways to solve this problem.The first one is to create callback functions as parameters for your existing functions.
function one(param_1 .. param_n, callback) {
var response = ... // some logic
if (typeof callback === "function") {
callback(response);
}
}
The second way is to use Promises like:
var p1 = new Promise(
function(resolve, reject) {
var response = ... //some logic
resolve(response);
}
}
var p2 = new Promise( ... );
p1.then(function(response1) {
p2.then(function(response2) {
//do some logic
})
})
You can try like this
var oneFinish = false;
var twoFinish = false;
function one(){
//...
oneFinish = true; // this value may depends on some logic
}
function two(){
//...
twoFinish = true; // this value may depends on some logic
}
function three(){
setInterval(function(){
if(oneFinish && twoFinish){
//...
}
}, 3000);
}
Since your functions func_one and func_two are making service calls you can call the functions on after other at the success callback of the previous function. Like
func_one().success(function(){
func_two().success(function(){
func_three();
});
});
I'm having a problem with callback functions in javascript. What I want to do is: loop on a for and call a function passing i as parameter. With that in mind, I have to loop to the next interaction only after the previous one has been finished. I don't know if this is a problem but inside the function I'm sending i as parameter, I have another callback function. Here is my code:
for(i=0; i<10; i++) {
aux(i, function(success) {
/*
* this should be made interaction by interaction
* but what happens is: while I'm still running my first interaction
* (i=0), the code loops for i=1, i=2, etc. before the response of
* the previous interaction
*/
if(!success)
doSomething();
else
doSomethingElse();
});
}
function aux(i, success) {
... //here I make my logic with "i" sent as parameter
getReturnFromAjax(function(response) {
if(response)
return success(true);
else
return success(false);
});
});
function getReturnFromAjax(callback) {
...
$.ajax({
url: myUrl,
type: "POST",
success: function (response) {
return callback(response);
}
});
}
jQuery's Deferred can be a bit tricky to get right. What you'll have to do is stack your promises in a chain. For example:
var
// create a deferred object
dfd = $.Deferred(),
// get the promise
promise = dfd.promise(),
// the loop variable
i
;
for(i = 0; i < 10; i += 1) {
// use `then` and use the new promise for next itteration
promise = promise.then(
// prepare the function to be called, but don't execute it!
// (see docs for .bind)
aux.bind(null, i, function(success) {
success ? doSomethingElse() : doSomething();
})
);
}
// resolve the deferred object
dfd.resolve();
for this to work, aux must also return a promise, but $.ajax already does this, so just pass it through and everything should work:
in aux:
function aux(i, callback) {
console.log('executing for `aux` with', i);
// return the ajax-promise
return getReturnFromAjax(function(response) {
callback(Boolean(response));
});
}
in getReturnFromAjax:
function getReturnFromAjax(callback) {
// return the ajax-promise
return $.ajax({
url: '%your-url%',
type: '%method%',
success: function (response) {
callback(response);
}
});
}
demo: http://jsbin.com/pilebofi/2/
I'd suggest that you'd look into jQuery's Deferred Objects and jQuery.Deferred()-method instead of making your own callback queue functions (as you are already using jQuery anyway).
Description: A constructor function that returns a chainable utility
object with methods to register multiple callbacks into callback
queues, invoke callback queues, and relay the success or failure state
of any synchronous or asynchronous function.
I don't have experience with jQuery, but your callback looks a bit fishy to me.
In plain JS I'd suggest trying something among the lines of this:
function yourMainFunction
{
function callbackHandler(result)
{
// Code that depends on on the result of the callback
}
getAjaxResults(callbackHandler);
}
function getAjaxResults(callbackHandler)
{
// Create xmlHttpRequest Handler, etc.
// Make your AJAX request
xmlHttp.onreadystatechange = function()
{
if (xmlHttp.readyState == 4 && xmlHttp.status==200)
{
// Do stuff you want to do if the request was successful
// Define a variable with the value(s) you want to return to the main function
callbackHandler(yourReturnVariable);
}
}
}
I have a situation where i am running a jquery function and in that function i have an each loop. The each loop takes some time for processing and that is why the next statement executes before each completes. This creates a problem for me. I want a function to execute when each completes. My function is as follows:-
function myFunc() {
// Do something
$.each(mylist, function (i, val) {
// do something
filepicker.store(myList, function (stored_fpfile) {
console.log("Store successful:", JSON.stringify(stored_fpfile));
}, function (FPError) {
// Error
}, function (progress) {
console.log("Loading: " + progress + "%");
}
);
});
CallMyFunction();
}
Call my function executes before each loop finishes.
I dont want to use count of the list to detect and run my procedure. I want a reliable solution.
I am using the InkFilePicker API to store files to Amazon
Any help is appreciated.
I would suggest generating deferred objects for each iteration that are then stored in an array. Then, after the each, you can wait until all those deferred objects are complete to run your other function.
function myFunc() {
// Do something
var promiseArray = $.map(mylist, function (i, val) {
return $.Deferred(function(def){
// do something
filepicker.store(val, function (stored_fpfile) {
def.resolve(stored_fpfile);
}, function (FPError) {
def.reject(FPError);
}, function (progress) {
def.notify(progress); // won't actually be accurate
});
}).promise();
});
$.when.apply($,promiseArray).done(function(){
console.log(arguments); // array of results from onSuccess callbacks
CallMyFunction();
})
}
Apparently, the store is a wrapper around an AJAX object. You should test the progress and when it is completed call CallMyFunction, like so before the call to each:
items = myList.length;
And inside each:
...
function (progress) {
console.log("Loading: " + progress + "%");
if (--items === 0)
{
CallMyFunction();
}
}
...
There aren't any counter arguments, performance is not impacted by this, you're sending data over the Internet which is the real bottleneck.
This is also reliable. In case of error you should decrement with --items too.