Situation
We have a function that does some processing. Then it executes a callback function.
1 2 3 4 5 6 7 |
function A(cbConfirmed) { // 1) process other code... var reply = cbConfirmed(); // 2) execute provided callback // 4) reply is 'true' } |
So say we call function A, and provide an anonymous callback function.
1 2 3 4 |
A (function() { // 3) callback definition return true; }); |
Under this case, we will get the true in function A’s definition.
Everything is synchronous so we go step by step.
The Problem
The problem happens when we have asynchronous code in our callback
Given definition A:
1 2 3 4 5 6 |
function A(cbConfirmed) { // process other code... var reply = cbConfirmed(); // execute provided callback } |
In our callback definition, we do something asynchrnous. It returns true after 2 seconds.
1 2 3 4 5 |
A (function() { setTimeOut(function() { return true; }, 2000 ); }); |
This is a problem because while the execution is happening for the setTimeOut of waiting 2 seconds, our main execution
continues and our reply will be undefined.
1 2 3 4 5 6 7 |
function A(cbConfirmed) { // process other code... var reply = cbConfirmed(); // execute setTimeOut of waiting 2 seconds // main execution will continue and 'reply' will be undefined } |
The Solution
The solution is to use Promises. In this particular case, I will use jQuery’s Deferred, which is exactly the same as Promise, except its packaged in jQuery’s API.
1) chain the ‘then’
In order to wait for the async to finish, we call the callback function, and then chain the API call ‘then’ in order to continue with the results. This is assuming we declared a Promise object and returned it in cbConfirmed(). (we’ll get to this in the next step)
When you chain the then call, it will wait for async code to finish, then you simply provide an anonymous callback function inside to get the results.
1 2 3 4 5 |
function A(cbConfirmed) { cbConfirmed().then(function(result) { console.log(result); }); } |
2) declare and return the Promise object
1 2 3 4 5 |
A(function() { var cbPromise = new $.Deferred(); return cbPromise; }); |
3) call resolve and use it where the async code has finished
1 2 3 4 5 6 7 8 9 10 |
A(function() { var cbPromise = new $.Deferred(); setTimeOut(function() { // after 2 seconds, we finish cbPromise.resolve('success! :D'); }, 2000); return cbPromise; }); |
The main execution will wait for the callback to finish. After 2 seconds, the callback returns and gives the result to function A.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
function A(cbConfirmed) { // 1) cbConfirmed().then(function(result) { // 6) console.log(result); }); } A(function() { // 2) var cbPromise = new $.Deferred(); // 3) setTimeOut(function() { // after 2 seconds, we finish // 5) cbPromise.resolve('success! :D'); }, 2000); // 4) return cbPromise; }); |