Asynchrony

You want to write efficient, responsive systems. Understanding asynchronous communication and programming models can help you do just that.

Background

In synchronous communication, the two parties must wait for each other to send and receive messages. The parties are present at the same time. Like a phone call.

In contrast, asynchronous communication allows one party to send a message without waiting for a response from the other party. Like sending an email.

In programming, asynchronous communication is nonblocking. The caller simply initiates a long running task (such as a file system or network request) and continues doing useful work without waiting for a result. The task happens in the background (perhaps on another thread). And it’s not just I/O that has unpredictable, long running completion times. A program should never block waiting for a user interaction or a timer to expire—these should be asynchronous, too.

If we get things right, asynchronous, nonblocking communication allows programs to be significantly more efficient and responsive. The I/O channels stay full, user interactions are processed promptly, and the overall system throughput increases.

There are two important concepts in asynchronous programming: events and promises. Let’s explore each of them.

Events

Asynchronous programming is often implemented with events. An event is something that happens, such as a user clicking a button, a timer expiring, or a file or network operation having completed.. Because events can happen at any time, from sources both within and outside of a program, systems that feature events are naturally concurrent! You’ll find events in multi-user and interactive systems (with user-generated events) and where I/O operations can occur over unpredictable durations and at unpredictable times.

An event object has properties such as its type (click, open, changed, keypress, etc.), the time at which it was generated, how it should behave (e.g., should it propagate?), and type-specific information such as the window coordinates for a click event, or the key code of a key event.

Events are created by event emitters and dispatched to event targets. You register event listeners (also called event handlers) with the targets. Listeners are functions that execute when the event lands at the target.

Here are some examples:

Browser Events

Here are some of the standard events in browser land:

Ambient Lightdevicelight
Battery statuschargingchange chargingtimechange dischargingtimechange levelchange
CSS Animationsanimationstart animationiteration animationend
CSS Transitionstransitionrun transitionstart transitionend transitioncancel
Clipboardcut copy paste
Device Orientationdevicemotion deviceorientation
Drag and Dropdragstart dragend dragenter dragleave drag dragover drop
Focusfocus blur
Formreset submit
Gamepadgamepadconnected gamepaddisconnected
Historypagehide pageshow popstate
IndexedDBcomplete success error abort versionchange upgradeneeded blocked
Inputchange input abort
Keyboardkeydown keypress keyup
Mediaseeking seeked suspend pause play waiting ended playing durationchange loadeddata emptied loadedmetadata ratechange timeupdate canplaythrough canplay stalled volumechange
Media Capturedevicechange
Misclanguagechange invalid input hashchange
Mouseclick dblclick mouseup mousedown mousemove mouseover mouseenter mouseout mouseleave wheel select contextmenu show
Networkonline offline
Notificationsnotificationclick
Pagevisibilitychange
Pointerlostpointercapture gotpointercapture pointerenter pointerleave pointermove pointerdown pointercancel pointerup pointerover pointerout pointerlockerror pointerlockchange
Printbeforeprint afterprint
Progressloadstart load progress timeout abort error loadend
Proximitydeviceproximity userproximity
Pushpushsubscriptionchange push
Resourceload beforeunload unload cached error abort
Resource Timingresourcetimingbufferfull
SVGbeginEvent endEvent repeatEvent SVGLoad SVGUnload SVGResize SVGScroll SVGZoom SVGError SVGAbort
Screen Orientationorientationchange
Selectionselectstart selectionchange
Server Sentopen message error
Service Workersmessage
Text Compositioncompositionstart compositionend compositionupdate
Touchtouchstart touchend touchmove touchcancel
Updatenoupdate obsolete updateready downloading checking error
Viewresize scroll fullscreenchange fullscreenerror
Web App Manifestappinstalled
Web Audioaudioprocess complete ended
Web Messagingmessage
Web Speechresume error error soundstart soundend speechstart speechend voiceschanged start end nomatch result mark boundary audioend audiostart start end pause
Web Storagestorage
Web Socketsopen message error close
Web Workersmessage
XMLHttpRequestreadystatechange

Many browsers, and their plugins, will create tons of additional non-standard events. You should checkout the Event Reference page at MDN, which lists well over 100 standard and non-standard events.

Here’s a simple example of handling a click event from JavaScript in a browser:

See the Pen Transition Demo by Ray Toal (@rtoal) on CodePen.

In the browser, the possible event targets are:

The methods on event targets are: addEventListener, removeEventListener and dispatchEvent.

Since the event objects are passed to the event listeners, you can read the event properties, as in this example:

<body>
  <p id="theP">Hello click me.</p>
  <script>
    document.getElementById('theP').addEventListener('click', (e) => {
      console.log(e.type, e.target.tagName, e.clientX, e.clientY, e.isTrusted);
    });
  </script>
</body>

When you click on the paragraph, you will see something like:

click P 28 16 true

But wait. That <p> element was nested inside a <body> element, so doesn’t the body element get the event, too? If so, which one—the inner or outer element—would get the event first?

Actually all of the possible targets get the event...twice! If you click on the paragraph element, a click event is sent first to the window, then to the document, then to the body, then to the paragraph, then to the paragraph again, then the body again, the document again, and finally to the window again. Did you notice how this event propagation happens in two phases? First, the event propagates outside-to-inside in the capturing phase, then inside-to-outside in the bubbling phase. Let’s explore:

events.html
<!doctype html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Exploring Events</title>
    <style>
      body, div, p, span {margin: 20px; padding: 8px}
      body {background-color: #eeffee}
      div {background-color: #ffffee}
      p {background-color: #eeffff}
      span {background-color: #ffeeff}
    </style>
  </head>
  <body id="thebody">
    <div id="thediv">
      Before
      <p id="theparagraph">Hello you can <span id="thespan">click here</span></p>
      After
    </div>
    <script src="events.js"></script>
  </body>
</html>
events.js
function say(arrow, e, name) {
  console.log(`${arrow} ${name} received ${e.type} during ${e.eventPhase}`);
}

function addListeners(el, name) {
  el.addEventListener('click', e => say('⬇︎', e, name), true);
  el.addEventListener('click', e => say('⬆︎', e, name), false);
}

document.querySelectorAll('[id]').forEach(el => addListeners(el, el.id));
addListeners(document, 'The document');
addListeners(window, 'The window');

Note the third argument to addEventListener: if true, the handler kicks in during the capture phase; if false, in the bubbling phase. We’ve added capture and bubble listeners to the document and all four elements, allowing us to see the propagation in action. Here’s what we see when we click on the innermost span:

capturebubble.png

This example shows off some properties of the event object:

Get the complete details at MDN or the DOM Living Standard.

Propagation can be controlled. Within a handler, you can call e.stopPropagation(). It does just what you think it does.

Exercise: Modify the above script so that propagation is stopped when reaching the div during the capturing phase.
Exercise: Modify the above script so that propagation is stopped when reaching the div during the bubbling phase.

You can programmatically fire events yourself with dispatchEvent. Such events are not trusted. This is best learned with an example. Trusted clicks come from your mouse, trackpad, or other real device, while a timer in our code dispatches nontrusted click events every few seconds:

<body>
  <p id="theP">Hello click me.</p>
  <script>
    const p = document.getElementById('theP');
    p.addEventListener('click', (e) => {
      console.log(e.isTrusted ? 'REAL CLICK' : 'FAKE CLICK!!');
    });
    // Send fake clicks every five seconds
    setInterval(() => p.dispatchEvent(new Event('click')), 5000);
  </script>
</body>

Node.js Events

Node.js is a JavaScript runtime system that provides access to files, processes, memory buffers, the console, the network, timers, etc.: all kinds of things for which event-driven programming shines. See the Node.js Events documentation.

Here are some of the standard events in Node.js:

Child Processclose disconnect error exit message spawn stdio
Clusterdisconnect exit fork listening message online setup worker
DNSlookup
Domainerror remove
HTTPclientError connect request response upgrade
HTTP2close connect frameError goaway localSettings remoteSettings stream
HTTP2 Sessionclose connect frameError goaway localSettings remoteSettings

In Node.js, events are a core part of the architecture. The EventEmitter class is used to create and manage events.

TODO - file system example

TODO - fetch from the web example

Pygame Events

TODO

Promises

TODO - this is when you want a result

Summary

We’ve covered:

  • Synchronous vs. Asynchronous Communication
  • Advantages of Asynchronous Programming
  • Events
  • Examples of Events