I have two callbacks that need to be put on the same change
event on the same item. For reasons that are not worth going into, I need to have these callbacks in separate on
, I cannot unify them under a single on
call. So I need these on
calls to stay separate, for example:
$('body').on('change', '#my-div', function1);
$('body').on('change', '#my-div', function2);
Now, I have an AJAX call inside function1
. I would like for function2
to always execute after the execution of function1
is done, including after an AJAX response has been received inside function1
. I asked ChatGPT and it advised me to use $.Deferred()
:
let function1Deferred = $.Deferred();
function function1() {
function1Deferred = $.Deferred();
$.ajax({
url: 'your-url', // Replace with your URL
method: 'GET',
success: function(data) {
console.log("Function1 AJAX request completed");
function1Deferred.resolve();
},
error: function() {
function1Deferred.reject();
}
});
}
function function2() {
function1Deferred.done(function() {
console.log("Function2 is now executing after function1 has completed.");
});
}
To my mind, though, this only solves the problem the first time the change
event gets triggered, because there’s no guarantee that function1
will run first, before function2
– and therefore there’s no guarantee that, on the second and third etc. trigger of the change
event, the line function1Deferred = $.Deferred();
inside function1
will run before function2
runs, so function2
might well be the first to run, and run to the end, on the subsequent triggers of the change
event.
Am I missing something, is the code actually achieving what I want and I’m just missing something about how Deferred
works under the hood? If yes, what am I missing? If not, how can I solve my problem and ensure function2
always runs after function1
on subsequent triggers of the event, not just on the first one?
I just want to emphasize again that I need the calls of the on
function to stay separate, I cannot use a single on('change', '#my-div', newFunction)
call, this is not a solution for my problem in its larger context.
2
Answers
As presented, the code will not necessarily do what you want. If
function2
triggers first:Here’s a demo which explicitly sets the deferred object to
null
to make it more obvious. If you click the "run F2+F1" button, you’ll get a "function1Deferred is null" error.Given the constraints, the simplest option is probably to wrap the
function2
body in asetTimeout
call:This will return control to the calling function, allowing the other handlers to execute, before registering the
done
callback on the deferred object.Updated demo
Now, whichever button you click, you will not see the "Running function 2" message until after the "Function 1 complete" message.
Also, as @jabaa mentioned in the comments,
$.Deferred
is a precursor to the JavaScript Promise. Combined with async functions, you could simplify this code even more: