1. A first stumbling step
As for many others, my encounter with asynchronous calls was puzzling at first.
I don't remember the details, but I may have tried something like:
let result;$.ajax({ url: 'https://jsonplaceholder.typicode.com/todos/1', success: function (response) { console.log('\nInside $.ajax:'); console.log(response); result = response; },});console.log('Finally, the result: '+ result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
Whoops!
The output of the lineconsole.log('Finally, the result: '+ result);
which I thought would be printed last, is printed before the other output!
– And it doesn't contain the result: it just prints undefined
. 1
How come?
A helpful insight
I distinctly remember my first aha (💡) moment about asynchronous calls.It was :
This is true in the example above.
2. Plain JavaScript and a callback function
Luckily, it is possible to write code after the asynchronous call that deals with the response once it has completed.
One alternative is the use of a callback function in a continuation-passing style :3
const url = 'https://jsonplaceholder.typicode.com/todos/2';function asynchronousFunc(callback) { const request = new XMLHttpRequest(); request.open('GET', url); request.send(); request.onload = function () { if (request.readyState === request.DONE) { console.log('The request is done. Now calling back.'); callback(request.responseText); } };}asynchronousFunc(function (result) { console.log('This is the start of the callback function. Result:'); console.log(result); console.log('The callback function finishes on this line. THE END!');});console.log('LAST in the code, but executed FIRST!');
.as-console-wrapper { max-height: 100% !important; top: 0; }
Note how the function asynchronousFunc
is void
. It returns nothing. asynchronousFunc
is called with an anonymous callback function,
(asynchronousFunc(function (result) {...});
).
This executes the desired actions on the result after the request has completed – when the responseText
is available.
Running the above snippet shows how I will probably not want to write any code after the asynchronous call (such as the lineLAST in the code, but executed FIRST!
).
Why?– Because such code will run before the asynchronous call delivers any response data.
Doing so is bound to cause confusion when comparing the code with the output.
3. Promise with .then()
The .then()
construct was introduced in the ECMA-262 6th Edition in June 2015.
The code below is plain JavaScript, replacing the old-school XMLHttpRequest with Fetch. 4
fetch('https://api.chucknorris.io/jokes/random') .then((response) => response.json()) .then((responseBody) => { console.log('Using .then() :'); console.log(responseBody.value +'\n'); });
.as-console-wrapper { max-height: 100% !important; top: 0; }
4. Promise with async
/await
The async
/await
construct was introduced in the ECMA-262 8th Edition in June 2017.
async function awaitAndReceivePromise() { const responseBody = ( await fetch('https://api.quotable.io/quotes/random') ).json(); console.log('Using async/await:'); const obj = (await responseBody)[0]; console.log('"'+ obj.content +'"–'+ obj.author +'\n');}awaitAndReceivePromise();
.as-console-wrapper { max-height: 100% !important; top: 0; }
A word of warning is warranted if you decide to go with the async
/await
construct.Note in the above snippet how await
is needed in two places.
If forgotten in the first place, there will be no output at all.If forgotten in the second place, the only output will be Using async/await:
– nothing else gets printed.
Forgetting the async
prefix of the function is maybe the worst of all – you'll get a "SyntaxError"
– and likely no hint about the missingasync
keyword.
All the above examples succinctly convey how asynchronous calls may be used on toyish APIs. 5
References
- Some questions and answers about asynchronous calls
- Using plain JavaScript and a callback function
- Continuation-passing style
- XMLHttpRequest at mdn web docs
- XMLHttpRequest: onload vs. onreadystatechange
- XMLHttpRequest.responseText
- An example demonstrating
async
/await
- Using the Fetch API
- Using promises
- The XMLHttpRequest Standard
- The Fetch Standard
- The Web Hypertext Application Technology Working Group (WHATWG)
- Links to ECMA specifications
- How can I fetch an array of URLs with Promise.all?
1Expressed by the asker of the question as they all return undefined
.
2Here is more on how asynchronous calls may be confusing at first.
3Like the X in AJAX, the name XMLHttpRequest
is misleading –it can be used to retrieve any type of data, not just XML.
These days, the data format of Web APIs is ubiquitously JSON, not XML.
4Fetch returns a Promise.I was surprised to learn that neither XMLHttpRequest nor Fetch are part of the ECMAScript standard.The reason JavaScript can access them here is that the web browser provides them.
The Fetch Standard and the XMLHttpRequest Standard are both upheld by the Web Hypertext Application Technology Working Group which was formed in June 2004. \
5You might also be interested inHow can I fetch an array of URLs with Promise.all?.