I have a function that stores several values from a HTML form, and that must work individually in order to store that info in any situation I need (ie before inserting on DB, or before udating info on DB...)
I need to be able to tell the system to execute this function ('storeValues'),and then execute any other (could be 'createNewClass', 'updateExistingClass'... whatever).
How can I sequence this? I tried here to store values first and, WHEN DONE, execute another function aleting about a value, but it says "storeValues() is not defined", and it is defined:
$('.tableClassHeader').on('click', '.createClass', function(){
storeValues().promise().done(function(){
createNewClass();
});
});
function storeValues(){
cl_year = $('.newClassForm').find('select[name=cl_year]').val();
cl_course = $('.newClassForm').find('select[name=cl_course]').val();
}
function createNewClass(){
alert(cl_year);}
I mean that storeValues function SHOULD BE a separate function with the possibility of being called from any other place, I know this problem could be solved by executing "createNewClass" from the "storeValues" function, but there will be times that I need to execute "updateClass" after "storeValues", not "createNewClass"
You can use a callback like this, if your storeValues is not synchronous like in your example:
$('.tableClassHeader').on('click', '.createClass', function(){
storeValues(createNewClass);
});
function storeValues(callback){
cl_year = $('.newClassForm').find('select[name=cl_year]').val();
cl_course = $('.newClassForm').find('select[name=cl_course]').val();
callback();
}
function createNewClass(){
alert(cl_year);
}
If it is synchronous, just calling createNewClass after storeValues is enough.
What this does is:
offers you the ability to pass a function of choice to the storeValues
inside storeValues it calls the callback function passed as parameter
If you need to execute your function with a different scope you can use call or apply.
Another way to do this, without callbacks would be using
http://api.jquery.com/promise/
http://api.jquery.com/jQuery.when/
http://api.jquery.com/deferred.promise/
Example as seen here http://jsfiddle.net/47fXF/1/ :
$('.tableClassHeader').on('click', '.createClass', function(){
$.when(storeValues()).then(createNewClass);
});
function storeValues(){
var dfd = new jQuery.Deferred();
setTimeout(function(){
console.log('storing values');
cl_year = $('.newClassForm').find('select[name=cl_year]').val();
cl_course = $('.newClassForm').find('select[name=cl_course]').val();
dfd.resolve();
}, 1000);
return dfd.promise();
}
function createNewClass(){
alert("trololo");
}
Added the setTimeout to simulate asynchronicity.
If your storeValues is making only one ajax request using jQuery, then you can return it directly as shown in the API documentation.
Also make sure to call resolve(), reject() appropriately.
Call like this . it first call the storeValues after executes the createNewClass function
$('.tableClassHeader').on('click', '.createClass', function(){
storeValues(function() {
createNewClass();
});
});
function storeValues(callback){
cl_year = $('.newClassForm').find('select[name=cl_year]').val();
cl_course = $('.newClassForm').find('select[name=cl_course]').val();
callback();
}
Related
I'm trying to understand JS and I'm really confused by the callback pattern.
function one(){
alert("I'm one!")
}
function total(arg, callback){
setTimeout(function() {alert("I'm "+arg);}, 1000);
callback();
}
total('all', one);
versus
function one(){
alert("I'm one!")
}
function total(arg){
setTimeout(function() {alert("I'm "+arg);}, 1000);
one();
}
total('all');
What is the benefit of passing one() as a parameter vs just calling it from within the function?
If you know that you're always going to call one, there's no need to accept it as an input parameter; you can just go ahead and call it.
The ability to accept callbacks allows you to easily write loosely coupled code.
You are, for instance, passing a callback to setTimeout in your code example. setTimeout knows to wait a given number of milliseconds before a function is called, but it doesn't know which function to call.
Passing in callback functions allows you to dynamically affect the flow of the program. Additionally, you could pass the outcome of total as a parameter to callback, which is often used to enable asynchronous programming.
function one(){
alert("I'm one!")
}
function total(arg, callback){
setTimeout(function() {
if (callback) {
callback();
}
}, 1000);
}
I suspect that your examples are not what was intended to showcase what a callback is. Does this make more sense?
function cb(arg){
alert("I'm "+arg+"!")
}
function total(arg, callback){
setTimeout(callback, 1000, arg);
}
total('one', cb);
Callback argument allows you to define custom interaction. It's commonly used with any asynchronous operation to allow reacting of state change (for example when operation is done, or errored).
One example of that may be an AJAX call (here with jQuery for simpliciy):
var request = $.ajax({
url: "script.php"
});
// this allows you to specify custom action handling
request.done(one);
I want to package a ajax call into an interface without then.
If i do like this, it will just return 'No ajax return';
var ajaxReturn = ajaxFunction();
function ajaxFunction(){
var text = 'No ajax return';
// get fileName using an ajax get
$.ajax();
return text;
}
If i do like this, it will be ugly for using then;
function ajaxFunction(){
var text = 'No ajax';
var dtd = $.Deferred();
$.ajax();
return dtd.promise();
}
$.when(ajaxFunction()).then();
I just want the interface to be simple and return the right thing, can i?
//return the right
var ajaxReturn = ajaxFunction();
function ajaxFunction(){
var text = 'No ajax';
var dtd = $.Deferred();
$.ajax();
return dtd.promise();
}
$.when(ajaxFunction()).then();
Whoa, what is all that? You do need .then but you don't need most of the surrounding stuff. $.ajax generates a promise for you. You don't need to make a promise object yourself. In fact, often the only reason you need to manually set up a Deferred/Promise directly is if you're using some library that sets up callbacks and doesn't use promises itself.
function ajaxFunction(){
return $.ajax();
}
ajaxFunction().then(function(data) { ... });
Now, let's say that you didn't actually want to return the JSON structure on the end of the ajax function; you want to take out just a number from inside of it, or tweak one value to make it an easier-to-use function for its callers. Easy enough:
function ajaxFunction(){
return $.ajax().then(function(data) {
return data[12].number;
}
}
ajaxFunction().then(function(number) { ... });
In direct answer to your question: No, what you asked for isn't possible. Whenever your JavaScript methods are running, the browser can't process other events like clicks and even basic scroll operations. So, any long-running operations (like contacting the server) do not return straight away, and instead offer a callback operation.
Well..., ajax is asynchronous so you either use .then() or use a callback logic... Doing synchronous ajax is not a option for me, so I won't even mention it.
The alternative to .then() would be something like this:
ajaxFunction(function(res){ // pass a function into it
// this will be called when the ajax is done
alert(res);
});
function ajaxFunction(callback){
// get fileName using an ajax get
$.ajax({
success: callback
});
}
But again, maybe you can use just a normal ajax callback pattern anyway
$.ajax({
...
success: function(res){
// use the res
}
});
Ajax is asynchronous. then is designed to make writing async operations look more similar to synchronous code and can actually be very elegant.
Additionally, $.ajax() returns a promise and is well suited to be written as follows:
function ajaxFunction(){
return $.ajax();
}
ajaxFunction().then(function(response){
// do whatever you want with the response
})
You simply can't write asynchronous code that way (ajaxResult = ajaxFunction()). The interpreter is going to keep trucking along line by line and ajaxResult will not be ready in time.
Read up on chaining $.Deferred's. It will really clean up your async code.
I have two JavaScript function in two different files. On click I am calling first function to delete and second function for refresh the text in the div. The problem is delete function is having confirm action. So if I call one after the other refresh function is executing immediately. But I want refresh function to execute after confirmation (Note:delete and refresh JavaScript functions are there in two different projects)
Below is the sample:
function deleteIFAsset(a) {
var id1 = a.id;
IframeCloudEditor.deleteFileFromTimeline(id1);
refreshAsseListt();
}
You'll have to use a callback. Any properly-designed library with asynchronous operations (like waiting for the user to confirm an action in an event-driven environment like a browser) should offer a callback for when that operation is complete. You haven't said what library the deleteFileFromTimeline comes from, but hopefully it offers a callback. The callback may be an argument you pass, or it may be part of a "promise" API.
If it accepts a callback directly, that would look something like this:
function deleteIFAsset(a) {
var id1 = a.id;
IframeCloudEditor.deleteFileFromTimeline(id1, function() {
refreshAsseListt();
});
}
or
function deleteIFAsset(a) {
var id1 = a.id;
IframeCloudEditor.deleteFileFromTimeline(id1, refreshAsseListt);
}
...if your refreshAsseListt (was that supposed to be refreshAssetList?) is compatible with what the library does with the callback (the arguments it passes to it and what it does with the return value).
If it returns a promise instead, that would look something like this:
function deleteIFAsset(a) {
var id1 = a.id;
IframeCloudEditor.deleteFileFromTimeline(id1).then(refreshAsseListt);
}
("Promises" are also sometimes called "futures" or "deferred objects.")
if you can change the code of deleteFileFromTimeLine, you can change it to return the result of the confirmation.
and execute refreshAsseListt.
example
function deleteFileFromTimeLine(ID)
{
...
return Confirm('Delete File?');
}
and change your code like this
function deleteIFAsset(a) {
var id1 = a.id;
if(IframeCloudEditor.deleteFileFromTimeline(id1))
{
refreshAsseListt();
}
}
You are searching for a solution, to execute your javascript synchronous. But javascript is always executed synchronously except for special cases like ajax requests or file access.
My advice is to use a callback function to solve this problem.
I have a functions which should run one AFTER the other, such:
function cutTomatoesAlone(Kg){
// slice my stuff
}
function cookTomatoes(Minutes){
// boil my stuff
}
I call them such:
cutTomatoesAlone(15) // 15kg, need 3 hours!
cookTomatoes(10); // need 10 minutes
But the cookTomatoes(10) finish before my cutTomatoesAlone(15).
How to run cutTomatoesAlone(15) first and when finished, then run cookTomatoes(10) ?
Edit: cutTomatoesAlone() load an external JSON. cookTomatoes(10) work on it.
Learn about promises and deferred objects. Every Ajax function in jQuery returns a promise, so you can easily chain your function calls.
For example:
function cutTomatoesAlone(Kg) {
return $.getJSON(...); // return the promise provided by $.getJSON
}
// called as
cutTomatoesAlone(15).then(function() { // attach callback
cookTomatoes(10);
});
In case of an Ajax call, the promise is resolved once the response was successfully retrieved.
You need the method The setTimeout() which will wait the specified number of milliseconds, and then execute the specified function.
function cutTomatoesAlone(Kg){
// slice my stuff
setTimeout(function() {
cookTomatoes(10)
}, delay);
}
If your functions are independent, it should work the way you expect, assuming you're not doing stuff like making http get requests asynchronously.
If you are, what you need to do is call the second function when the first one returns from its request, using JQuery's $.done() function.
Give cutTomatoesAlone a callback.
var cookingTimePerKg = 10;
function cutTomatoesAlone(Kg, Callback) {
// slice my stuff
// when done and a callback is defined do the callback
if(Callback) Callback(Kg*cookingTimePerKg);
}
Then you could do the following:
cutTomatoesAlone(15, cookTomatoes);
The callback could also be fired on the onComplete of the (potential) XHR request.
Some Function object prototype tuning would make it easier to read
Function.prototype.after = function(callback){
this();
if( typeof(callback) == "function")
callback();
}
a = function(){alert(1)};
a.after( function(){alert(2)} )
So with cooking subject:
var cutThem = function(){
cutTomatoesAlone(15) // 15kg, need 3 hours!
}
cutThem.after( function(){
cookTomatoes(10);
});
this is a proposal for general purpose, when ajax loads are on the game it's better to use their "whenDone" option to supply them a callback.
$("#basket").load("url.extension", {kilos: kg},
function(){
cookTomatoes(10);
});
I have two functions one of which includes multiple json call which are post by nature.
I want these to be synchronous. That is, one should run only upon the completion of the previous post (and if all posts are done and successful I want the second function to fire).
The code structure is somewhat like this:
$.getSomeData = function() {
$.postJSON("iwantdata.htm",{data:data},function(data)){
});
$.postJSON("iwantmoredata.htm",{data:data},function(data)){
});
});
$.useSomeData = function() {
});
The useSomeData must work upon subsequent json calls.
Can anyone please help me? Thanks in advance.
So basically you want something like this:
function chainPost(url1, url2, initialInput, func) {
$.post(url1, {data: initialInput})
.done(function (initialOutput) {
$.post(url2, {data: initialOutput})
.done(function (secondOutput) {
func(initialOutput, secondOutput);
});
});
}
chainPost("iwantdata.htm", "iwantmoredata.htm", 0, function (first, second) {
alert(first);
alert(second);
});
You can just nest them, starting the 2nd one in the completion function of the first and so on:
$.getSomeData = function() {
$.postJSON("iwantdata.htm",{data:data},function(data) {
$.postJSON("iwantmoredata.htm",{data:data},function(data)){
// use the data here
});
});
};
When dealing with asychronous functions, you cannot write code such as:
$.getSomeData();
$.useSomeData();
By definition, the first is asynchronous so it will not have completed yet with the second function is called and javascript does not have the ability to stop JS execution until an asynchronous operation is done.
You could pass your use function to the get function and then it would get called when the data was available as an addition to the above example like this:
$.getSomeData = function(fn) {
$.postJSON("iwantdata.htm",{data:data},function(data) {
$.postJSON("iwantmoredata.htm",{data:data},function(data)){
fn(data);
});
});
};
Then, you'd have a getSomeData(useFn) function that would take an argument of the function to call when all the data was ready.
Deferred objects [docs] are perfect for this. Unfortunately, your code example contains syntax errors and it is not clear how the calls are nested. So, I'm not sure if you want to run both Ajax calls after one another or parallel, but either way is possible.
Here are two examples. Have a look at the documentation for more information and play around with it.
Note: .postJSON is not a built in jQuery method, I assume here that you are returning the return value from the $.ajax (or $.post) function.
Parallel Ajax calls:
$.getSomeData = function() {
var a = $.postJSON("iwantdata.htm", {data:data});
var b = $.postJSON("iwantmoredata.htm", {data:data});
// return a new promise object which gets resolved when both calls are
// successful
return $.when(a, b);
};
// when both calls are successful, call `$.useSomeData`
// it will have access to the responses of both Ajax calls
$.getSomeData.done($.useSomeData);
See: $.when
Chained Ajax calls:
... where the response of the first call is the input for the second one. This is only an example, of course you can pass any data you want.
$.getSomeData = function() {
return $.postJSON("iwantdata.htm", {data:data}).pipe(function(response) {
// execute the second Ajax call upon successful completion
// of the first one
return $.postJSON("iwantmoredata.htm", {data:response});
});
};
// if both Ajax calls are successful, call `$.useSomeData`
// it will have access to the response of the second Ajax call
$.getSomeData.done($.useSomeData);
See: deferred.pipe()
If you have a more complex logic, you can also create, resolve or reject your own deferred objects. Have a look at the examples in the documentation.