JavaScript: Asynchronous Iteration
Page 24
Asynchronous Iteration
Introduction
This page provides interactive examples and code, demonstrating asynchronous iteration implemented with ECMAScript 2018. The examples build from fairly simple to more involved, throughout the tutorial.
Simple fetch
Start with the simple fetch
API.
Anonymous method fetch.then((response) =>
,
activates after the fetched file downloads.
Within the then()
method, this example
displays the response status and JSON text,
to an HTML element.
Next wrap similar code in an AsyncGenerator
function. Call the function, then display the parsed JSON file
along with an image. The image source string downloads with the JSON file.
Keywords async
and await
cause the JSON file to download on a background thread.
Simple Generators, Iterators
Next we'll implement one simple
Generator function, while demonstrating
two applications of that function. Generator functions
return a value after the yield
keyword.
Generator functions also maintain state. Built-in code
remembers which yield
value was returned last.
Call generator functions with a few different
techniques. The first function call simply
retrieves incremental values from yield
with the next()
method,
each time you tap a button.
The second generator function iterates with a
for
loop, using the function's sychronous iterator.
Asynchronous Iterators
Last we'll combine the fetch API with keywords
async
and wait
on a background thread.
Asynchronous
generators yield
properties
in response to next()
method calls,
similar to synchronous generators.
However each call to next()
should process on a background thread.
The two examples in this tutorial demonstrate
two new techniques. The first example
displays all data after
waiting for a file to download.
The second example calls the asynchronous function
within a for
loop.
Asychronous Downloads
The previous Asynchronous Downloads
page
in this tutorial, explained
how to download XML files asynchronously
with XMLHttpRequest
.
However that project
stressed simulated threading. The new
Promise
object with fetch
API
may bring real multi threading to JavaScript.
According to Mozilla tutorials, Promise
with
async
and await
may actually operate
on a separate thread. As far as I know, JavaScript doesn't
actually process on more than one thread. However,
that topic should be tested now.
Asynchronous Versus Synchronous
Synchronous
Historically JavaScript operates on one thread. One thread means that all function, method and other JavaScript calls happen in a sequence. If one method takes a long time to complete, then the next coding steps must wait.
In the past, asynchronous
JavaScript simply appeared
to operate on separate threads.
For example, a long running method might operate for a slice of time,
let the user click buttons,
then return to waiting
while the long running method continues processing.
Such code appears as if the long
running method's allowing user interface responsivness
however you'll have to wait for a response to button clicks.
Asynchronous
Asynchronous processing allows two or more different operations to process at the same time. Most operating systems include multiple processors. When developers apply asychronous processing some methods, or long running functions, might run on one thread, while user interface features run on another thread. Two or more tasks can happen simultaneously which often allows for a more user friendly, responsive application.
However multiple threaded programming requires care to avoid race conditions and dead lock.
fetch API
In the previous tutorial Asynchronous Downloads
,
we used the XMLHttpRequest
object.
Here we'll use the fetch
API, which
has improved upon XMLHttpRequest
.
PHP JSON File
The following PHP file,
javascript21b-json-simple.php
implements an object with fields,
name, pop, region
and image
.
Then PHP function json_encode()
prepares the fields in JSON format.
Method echo
simply
displays our JSON file.
The PHP file's retrieved with fetch
for the
yellow
and
lavender
buttons.
Continue reading for details regarding how
to download
javascript21b-json-simple.php,
with the fetch API.
<?php $animalObj->name = "American Bison"; $animalObj->pop = 1500; $animalObj->region = "Custer State Park, South Dakota"; $animalObj->image ="assets/bison.jpg"; $animalJSON = json_encode($animalObj); echo $animalJSON; ?>
Threading Example
The following code downloads
PHP file
javascript21b-json-simple.php,
in the background.
When the file's fetched
, or downloaded,
you'll see the response status
and the JSON text
in the lavender fetch button.
The fetch
API activates method then()
after fetch processing has completed. While fetch processes,
the user interface should remain responsive, as fetch operates on
a background thread.
With proper threading, developers can accomplish other tasks, while waiting
for then()
to process.
Async, Await, Promise, Yield
See the JavaScript file
async-fetch.js
for implementation that applies to the yellow and violet buttons.
Tap the lavender button
for an example that calls method fetch.then()
.
Tap the yellow button
for an example that activates the fetch
API.
Function async function* jsonFromFile(fileUrl)
returns an AsyncGenerator
just once
with keywords yield, await
and the Promise
object.
However learn more about Async, Await, Yield, Promise below. The combination's much more useful with multiple calls to the same generator function.
Lavender Button Code
The following JavaScript programs the
lavender button.
JavaScript implements the simple fetch
API
with method then()
.
// Button one: var eFetch = document.getElementById( 'eFetch' ); // Button two: var eFetch2 = document.getElementById( 'eFetch2' ); // Image: var im = document.getElementById('im'); // JSON PHP file to fetch: var fileUrl = 'https://code.7thunders.biz/h5/js/javascript21b-json-simple.php'; // String data retrieves // from downloaded JSON file. var sName = ''; var sPop = ''; var sRegion = ''; var imsrc = ''; /** * run at startup. */ const fetchPromise = fetch( fileUrl ); // Display just fetch object // to first button: eFetch.innerHTML = fetchPromise; // Display data // after it's received: fetchPromise.then((response) => { eFetch.innerHTML = "Received response text: "+response.statusText; eFetch.innerHTML += "<br>Status code: "+response.status; eFetch.innerHTML += "<br>JSON: "+response.json(); });
Async, Await, Yield, Promise
The yellow button downloads the same PHP file with JSON,
javascript21b-json-simple.php.
However an async
function activates after you tap the
yellow fetch button.
Code applies async, await
and yield
with the Promise
object just once, as an introduction.
However yield
with AsyncGenerator
allow developers to display more than one section of asynchronous content, in a sequence.
Read the JavaScript for simple async, await, yield and promise, below. Optionally download the JavaScript file, async-fetch.js. Continue reading for more useful examples.
Async
Modify asynchronous functions with the
async
keyword.
Any function which implements await
must
implement the async
keyword.
The keyword await
causes async
functions to wait while they process data in a background thread.
async function* jsonFromFile(fileUrl
The async
keyword tells us that
code will call this function, then return
to accomplish other tasks.
The function itself processes in the background,
while the interface can respond to user interactions.
Await
Asynchronous functions apply the
await
keyword to wait
while processing in a separate thread.
The following code waits while the browser downloads a file.
const fetchPromiseJSON = await fetch(fileUrl);
The await
keyword indicates that this
line, or block, of code will wait within its
background thread, until it finishes processing.
In this case we're just waiting to download the PHP file described above.
Yield
The yield
keyword pauses and returns a value from a generator
function.
yield await Promise.resolve(fetchPromiseJSON.json());
Developers can call AsyncGenerator
functions multiple times
with the next()
method.
Each time the function executes, the next yield line returns a new value.
When all yields have completed then the returned object's done
property equals true
.
Promise
The file
async-fetch.js
implements a simple example with async, await, yield
and the Promise
object.
However Promise
returns an AsyncGenerator
which allows multiple threaded calls to method next()
, within the same function;.
JavaScript: Await, Async, Yield Promise
JavaScript that responds to click events for the yellow button, displays below.
Function jsonFromFile()
returns one
yield value asynchronously.
The next section explains how to return a series
of yield values asynchronously.
Notice the function's preceded with keyword
async
and the signature includes an asterisk,
function*
.
async function* jsonFromFile(fileUrl)
JavaScript for Yellow Button
The following JavaScript processes click events on the yellow button. JavaScript file javascript/async-fetch.js, implements the following code.
// Button one lavender: var eFetch = document.getElementById( 'eFetch' ); // Button two yellow: var eFetch2 = document.getElementById( 'eFetch2' ); // Image: var im = document.getElementById( 'im' ); // JSON PHP file to fetch: var fileUrl = 'https://code.7thunders.biz/h5/js/javascript21b-json-simple.php'; // Prepare variables // to display data: var sName = ''; var sPop = ''; var sRegion = ''; var imsrc = ''; ... /** * run when when * button's tapped. */ function btnFetch(){ formatJson(); } /*** * wait for file * with fetch and await. * function* indicates * Return an AsyncGenerator. */ async function* jsonFromFile(fileUrl){ const fetchPromiseJSON = await fetch(fileUrl); // Waiting happens here // No need for then. // Returns an AsyncGenerator object // async iterable and async iterable // protocol: yield await Promise.resolve(fetchPromiseJSON.json()); } /** * Asynchronously display * JSON properties and attributes. */ async function formatJson(){ // Returns JSON name value objects: for await (const obj of jsonFromFile(fileUrl)) { sName = obj['name']; sPop = obj['pop']; sRegion = obj['region']; sImgSrc = obj['image']; } / // Display the image: im.src = sImgSrc; // Display the data: eFetch2.innerHTML = "Name: "+sName+", Population:"+sPop; eFetch2.innerHTML += ",Region:"+sRegion; }
When the file's fetched
, or downloaded,
you'll see JSON values from JSON attributes
in the yellow fetch button.
You'll see an
image
retrieved from the image
attribute
of the JSON object.
Fetch Simple & PHP JSON
The lavender button automatically fetches and displays simple data from a PHP file. Tap the yellow button. JavaScript retrieves and displays data from the same PHP file, however JSON fields are parsed and image source from JSON displays in an image element below the buttons.
Generator Functions
This section demonstrates simple, one threaded, generator functions.
The asterisk after the keyword function
,
indicates this is a Generator function.
Function displayCard()
returns a Generator
object.
function* displayCard ()
Yield
Call method next() on the
Generator object.
The Generator function returns
a value declared after the keyword, yield
.
yield "King";
Method next()
Please notice the async generator, function* displayCard()
saves to a constant as follows.
const genCards = displayCard();
Within function function displayCardNext()
call the AsyncGenerator's next()
method as follows. Each call retrieves the yield
value from the next yield
line
within method function* displayCard()
.
var obj = genCards.next();
Tap a Button to Activate Generator Functions
Tap the
blue button
once for each yield value.
Tap the
red button
once for all yield values.
The red button calls Generator method next()
,
in a loop to display every yield value quickly.
JavaScript Generator Functions
JavaScript, with generator functions, displays below. The code below responds to click events on the blue and red buttons above.
JavaScript for Generator Functions
The following JavaScript implements Generator functionality for the blue and red buttons. See JavaScript file async-iterate.js.
// HTML elements to // display data: var eGen = document.getElementById( 'eGen' ); var eGenIt = document.getElementById( 'eGenIt' ); // Image to display: var imAsync = document.getElementById( 'imAsync' ); /*** * The asterisk after keyword function * indicates this is a Generator function. * This function returns a * Generator object. */ function* displayCard () { // yield, like return yield "Ace"; yield "King"; yield "Queen"; yield "Jack"; } // Generator function // to display a series of cards. const genCards = displayCard(); /** * Generator, genCards, returned from * Generator function */ function displayCardNext(){ // Access property, value: var obj = genCards.next(); var n = obj.value; var d = obj.done; if(d == false){ eGen.innerHTML += n +","; } } /** * Iterator from Generator * directly from function. */ function displayCardNextIt(){ eGenIt.innerHTML = ""; for (const val of displayCard()) { eGenIt.innerHTML += val +","; } }
Asynchronous Iteration
We started with asynchronous downloads. The previous section explained interation functions. This section combines both topics. We want iteration over values which download in the background. With any large processes users can continue with a responsive interface while background data downloads in a separate thread. The separate thread allows asynchronous processing.
Tap the aqua button, to see inspirational quotes downloaded from a text file. The aqua button downloads the file asynchronously, splits the file into lines, then displays the data on the user interace thread.
Tap the green button, to see the same set of inspirational quotes. However the green button downloads the text file, splits the file into an array of strings, then displays the strings with an asynchronous iterator.
JavaScript Asynchronous Iterator
The following JavaScript code, from file async-iterate-yield.js, implements the download and display of data that you see in the aqua and green buttons above.
Now you've progressed from simple asynchronous fetch operations, to synchronous generators and finally asynchronous generators with fetch operations.
const S_STAR = "<br>*******<br>"; // HTML elements to // display data: var eGenA = document.getElementById( 'eGenA' ); var eGenItA = document.getElementById( 'eGenItA' ); var fileAsync = 'https://code.7thunders.biz/h5/js/assets/inspireAll.txt'; var sAInfo = null; /** * Respond to button tap. */ function getText() { loadText(); } /** * Fetch a text file * on the background thread. * Split the file into lines. * Display lines on the * user interface thread. */ async function loadText(){ let myObject = await fetch(fileAsync); let sInfo = await myObject.text(); let s = sInfo.split('\n'); display(s); } /** * Fetch file. * Split file. * Iterate over * split array asynchronlusly. */ async function loadTextLines(){ let myObject = await fetch(fileAsync); let sInfo = await myObject.text(); sAInfo = await sInfo.split('\n'); eGenItA.innerHTML = ""; // Run AsyncGenerator for (const val of iterateTextLines()) { eGenItA.innerHTML += S_STAR + val + S_STAR; } } /*** * Yield array element by array element. * @return: AsyncGenerator */ function *iterateTextLines(){ yield sAInfo[0]; yield sAInfo[1]; yield sAInfo[2]; } /*** * Just display to HTML element eGenA. * @param s: array of strings. */ function display(s){ eGenA.innerHTML = ""; for(var i = 0; i < s.length; i++){ eGenA.innerHTML += S_STAR + s[i] + S_STAR; } }
Summary
This page provided interactive examples and code, demonstrating asynchronous iteration implemented with ECMAScript 2018. The examples expanded from fairly simple to more involved, throughout the tutorial.
Simple fetch
You started with the simple fetch
API.
Anonymous method fetch.then((response) =>
,
activates after the fetched file downloads.
Within the then()
method, this example
displayed the response status and JSON text,
to an HTML element.
Next you wrapped similar code in an AsyncGenerator
function. Call the function, then display the parsed JSON file
along with an image.
The image source string downloads with the JSON file.
Keywords async
and await
cause the JSON file to download on a background thread.
Simple Generators, Iterators
Next we implemented one simple
Generator function, while demonstrating
two applications of that function. Generator functions
return a value after the yield
keyword.
Generator functions also maintain state. Built-in code
remembers which yield
value was returned last.
Call generator functions with a few different
techniques. The first function call simply
retrieved incremental values from yield
with the next()
method,
each time you tap a button.
The second generator function iterates with a
for
loop, using the function's sychronous iterator.
Asynchronous Iterators
Last we combined the fetch API with keywords
async
and wait
on a background thread.
Asynchronous
generators yield
properties
in response to next()
method calls,
similar to synchronous generators.
However each call to next()
should process on a background thread.
The two examples in this tutorial demonstrate
two new techniques. The first example
displays all data after
waiting for a file to download.
The second example calls the asynchronous function
within a for
loop.
Feel free to download and use the code.
Learn JavaScript
JavaScript's the foundation of Web developer and Website design skills. This free and unique JavaScript tutorial includes some new or seldom used, but useful features.