Sequential execution

5.7 Sequential execution

Promise chains contain promises which are executed in a sequential way. This means that a promise in the chain is not executed until the preceding promise in the chain has been fulfilled. We can use loops or a recursive function to run some promises in a sequential way. This is useful when dealing with a promise chain with dynamic length.

Getting Ready

All you need to implement this recipe is an installation of TypeScript 2.0 or higher and jQuery.

How to do it

We are going to write a promise to load some files via AJAX:

  const files = [
    '/data/countries.json',
    '/data/user_profile.json'
  ];

  function loadFile(file) {
    return new Promise((resolve, reject) => {
      $.ajax({
        url: file,
        async: true,
        method: 'GET',
        dataType: 'json',
        success: function (data) {
          resolve(data);
        },
        error: function (e) {
          reject(e);
        }
      });
    });
  }

We are going to also declare a helper function to execute a promise for each file to be loaded:

  function runSequence(array, callback) {
    return new Promise((resolve, reject) => {
      const results = [];

      function chain(array, index) {
        if (index === array.length) return resolve(results);
        Promise.resolve(callback(array[index]))
          .then((content) => {
            results.push({success: true, result: content});
            chain(array, index + 1);
          })
          .catch((e) => {
            results.push({success: false, result: e});
            chain(array, index + 1);
          });
      }
      chain(array, 0);
    });
  }

We are now ready to load our files in using sequential execution:

runSequence(files, loadFile).then((contents) => {
  console.log(contents);
});

How it works

The number of files to be loaded is configurable and each file is loaded using a promise. Because the number of files is configurable we are dealing with a promise chain with dynamic length. If the files can be loaded in parallel we should try to run the promises using parallel execution but let’s imagine that the files must be loaded in a particular order for the sake of this example. The sequential execution of the promises is achieved thanks to the usage of a recursive function named chain which iterates through all the promises in the chain.

Source Code

Sequential execution

See also

Please refer to the other recipes about promises in this chapter to learn about more bluebird and the promises API.

Chaining promises

5.6 Chaining promises

When we need to run a few asynchronous operations in certain order, we can use chained promises to use the output of a promise as the input of the following promise.

Getting Ready

All you need to implement this recipe is an installation of TypeScript 2.0 or higher and jQuery.

How to do it

We are going to use once more the getProfileAsync function:

import * as Promise from 'bluebird';
import * as $ from 'jquery';

interface IUserProfile {
    name: string,
    surname: string,
    website: string,
    twitter: string
  }

function getProfileAsync() {
    return new Promise((resolve, reject) => {
      $.ajax({
        url: '/data/user_profile.json',
        async: true,
        method: 'GET',
        dataType: 'json',
        success: function (data: IUserProfile) {
          resolve(data);
        },
        error: function (e) {
          reject(e);
        }
      });
    });
  }

We are also going to declare two new asynchronous operations. The first function transforms the JSON data returned by the getProfileAsync function into HTML. The second function appends the HTML to a HTML element in the DOM.

function generateHTMLAsync(data: IUserProfile) {
    return new Promise((resolve, reject) => {
      const html = `<div>
              <h3>${data.name}</h3>
              <p>${data.name}</p>
              <h3>${data.surname}</h3>
              <p>${data.surname}</p>
              <h3>${data.website}</h3>
              <p>${data.website}</p>
              <h3>${data.twitter}</h3>
              <p>${data.twitter}</p>
            </div>`;
      resolve(html);
    });
  }

function appendHTMLAsync(html: string) {
    return new Promise((resolve, reject) => {
      const $container = $('#user_account');
      $container.html(html);
      resolve($container);
    });
  }

Now that we have three asynchronous tasks we can chain then as follows:

getProfileAsync()
  .then(generateHTMLAsync)
  .then(appendHTMLAsync)
  .catch((e) => {
    console.log(e);
  });

How it works

The promises API allow us to use the then and catch methods to create chain of promises. The preceding code snippet executes the getProfileAsync tasks and when it is fulfilled pass its output as the input of the following tasks in the chain: generateHTMLAsync. Finally, when the generateHTMLAsync tasks has been completed, the appendHTMLAsync task is executed. If any errors take place during the execution the chain of promises, the function passed to the catch method is invoked.

There’s more

In the preceding example we have passed a function which return a promise to the then method but it is also possible to pass a function which returns a value instead. The then method creates a promise using this value. The following code snippet can help us to understand how we can pass the output of a promise as the input of the following promise and what happens when a promise has not fulfillment value:

Promise.resolve('step-1-result').then(
    function step2(preiousResult) {
      console.log(`step 2 args: ${preiousResult}`);
      return 'step-2-result';
    }
  ).then(
    function step3(preiousResult) {
      console.log(`step 3 args: ${preiousResult}`);
      // no return
    }
  ).then(
    function step4(preiousResult) {
      console.log(`step 4 args: ${preiousResult}`);
      return Promise.resolve('step-4-result');
    }
  ).then(
    function step5(preiousResult) {
      console.log(`step 5 args: ${preiousResult}`);
    }
  );

The preceding code snippet displays the following in the browser console when executed:

step 2 args: step-1-result
step 3 args: step-2-result
step 4 args: undefined
step 5 args: step-4-result

Source Code

Chaining promises
Chaining promises (more)

See also

Please refer to the other recipes about promises in this chapter to learn about more Bluebird and the promises API.

Promises with multiple consumers

5.5 Promises with multiple consumers

Sometimes we need to consume the result of an asynchronous operation from two or more pieces of code. In that case we say that a promise has multiple consumers.

Getting Ready

All you need to implement in this recipe is an installation of TypeScript 2.0 or higher and jQuery.

How to do it

When two or more pieces of code need to consume the result of an asynchronous operation we can share a promise between them. The following example declares a function named getProfileAsync. This function returns a promise that returns the details of a user profile when fulfilled:

import * as Promise from 'bluebird';
import * as $ from 'jquery';

interface IUserProfile {
	name : string,
	surname : string,
	website : string,
	twitter : string
}

function getProfileAsync() {
	return new Promise((resolve, reject) => {
		$.ajax({
			url: '/data/user_profile.json',
			async : true,
			method: "GET",
			dataType : "json",
			success: function(data : IUserProfile) {
				resolve(data);
			},
			error : function(e) {
				reject(e);
			}
		});
	});
}

We can then obtain one single promise and share it with other pieces of code:

function renderMenuBar(profilePromise) {
	profilePromise.then((data : IUserProfile) => {
		$("#user_name").html(data.name);
	}).catch((e) => { 
		console.log(e);
	});
}

function renderAccountPage(profilePromise) {
	profilePromise.then((data : IUserProfile) => {
		$("#user_name").html(data.name);
		$("#user_surname").html(data.surname);
		$("#user_website").html(data.website);
		$("#user_twitter").html(data.twitter);
	}).catch((e) => { 
		console.log(e);
	});
}

const profilePromise = getProfileAsync();
renderMenuBar(profilePromise);
renderAccountPage(profilePromise);

How it works

The preceding code snippet declared two functions named renderMenuBar and renderAccountPage. These two functions need to access the result of a promise to display some details on screen. We can achieve this by passing the promise to both functions.

Source Code

Promises with multiple consumers

See also

Please refer to the other recipes about promises in this chapter to learn about more Bluebird and the promises API.

Promises

5.4 Promises

The promises API allow us to represent an operation that has not been performed yet. Promises are supported by some web browsers today but it is recommended to use a promises library like Q.js or Bluebird. These libraries are compatible with older browsers and include some helpers that will help us to minimize the complexity of our asynchronous code.

Getting Ready

All you need to implement this recipe is an installation of TypeScript 2.0 or higher and bluebird. We can install bluebird and its type declaration files using npm:

$ npm install bluebird --save
$ npm install @types/bluebird –-save-dev

How to do it

We are going to re-implement the example that we implemented in the recipe about callbacks in this chapter. This time we are going to use promises instead of callbacks:

import * as Promise from 'bluebird';

function doSomethingAsync() {
  return new Promise((resolve, reject) => {
    console.log('do something...');
    resolve('result');
  });
}

How it works

To create a promise, we need to create an instance of the Promise class. Some browsers include native support for the Promise class but we are going to use the bluebird promises library. The function named doSomethingAsync returns an instance of the Promise class. Promise objects have an estate that can be pending, fulfilled or rejected. When we create an instance of the Promise class the promise estate is pending. Once a promise has been fulfilled or rejected its state cannot change again. The Promise class constructor takes a function as its only argument, which takes two functions names resolve and reject as its arguments:

  • Resolve: When this function is invoked the promise state changes from pending to fulfilled, which means that the promise operation was completed successfully. We can pass the result of the operation performed by the promise to this function.
  • Reject: When this function is invoked the promise estate changes from pending to rejected, which means that something went wrong during the execution of the promise. The details abut the error can be passed to this function.

    It is recommended to append “Async” at the end of the named of asynchronous functions (functions that return a promise) as a naming convention because it helps developers to understand the behavior of the function even when no documentation is available.

Inside the promise body we can perform any kind of operation. In the preceding code snippet, we are just displaying some text in console and invoking the resolve function. If we invoke the function its return will be the promise object with pending state:

const promise = doSomethingAsync();

The promise object allows us to use the methods named then and catch, which allow us to add event handlers that will be invoked when the promise state changes from pending to fulfilled or rejected respectively:

promise.then(function() {
	console.log('done!');
}).catch(function() {
	console.log('error!');
});

It is also possible to do it inline:

doSomethingAsync().then(function() {
	console.log('done!');
}).catch(function() {
	console.log('error!');
});

There’s more

We will now re-implement the AJAX helper that we previously implemented in this chapter using the Promises API:

import * as Promise from 'bluebird';

interface IAjaxOptions {
    url: string,
    async: boolean,
    method: string,
    dataType: string
  }

function ajaxAsync(options: IAjaxOptions) {
    return new Promise((resolve, reject) => {

      const request = new XMLHttpRequest();
      request.open(options.method, options.url, options.async);

      request.onload = function () {
        if (request.status >= 200 && request.status < 400) {
          // Success!
          const data = null;
          if (options.dataType === 'json') {
            const data = JSON.parse(request.responseText);
          }
          resolve(data);
        }
        else {
          // We reached our target server, but it returned an error
          reject(request.responseText);
        }
      };

      request.onerror = (e) => {
        reject(e);
      };

      request.send();
    });
  }

Once we have implemented the function, it can be invoked to access the promise object. We can then use the the then and catch methods as follows:

ajaxAsync({
    url: '/data/countries.json',
    async: true,
    method: 'GET',
    dataType: 'json'
  }).then(function (data) {
    // Success!
    console.log(data);
  }).catch(function (e) {
    // There was a connection error of some sort
    console.log(e);
  });

Source Code

Promises
Promises (AJAX)

See also

Please refer to the other recipes about promises in this chapter to learn about more bluebird and the promises API.

Callbacks

5.3 Callbacks

A callback is a function that is passed as an argument to another function, which is expected to call back (execute) the argument at some convenient time.

Getting Ready

All you need to implement this recipe is an installation of TypeScript 2.0 or higher and jQuery.

How to do it

We can pass a callback (a function) to another function as follows:

function doSomething(callback: () => void) {
  // do something...
  callback();
}

How it works

In TypeScript functions are first-class citizens. This means that we can use functions just like any other value. Functions can be assigned to variables:

const callback = function() {
	console.log('called back!');
};

Functions can also be passed as arguments to another function:

doSomething(callback);

In the preceding line we have passed the variable named callback but it is also possible to pass a function which has not been previously defined:

doSomething(function() {
	console.log('called back!');
});

Remember that you can use arrow functions to declare anonymous functions. This is especially useful when declaring callbacks as inline functions:

doSomething(() => console.log('called back!'));

There’s more

We will now try to implement something similar to the jQuery AJAX function from scratch. In this example two callbacks named success and error are passed to a function named ajax as part of an object literal named options:

interface IAjaxOptions {
    url: string,
    async: boolean,
    method: string,
    dataType: string,
    success: (data: any) => void,
    error: (error: any) => void
  }

function ajax(options: IAjaxOptions) {
    const request = new XMLHttpRequest();
    request.open(options.method, options.url, options.async);

    request.onload = function () {
      if (request.status >= 200 && request.status < 400) {
        // Success!
        let data = null;
        if (options.dataType === 'json') {
          data = JSON.parse(request.responseText);
        }
        options.success(data);
      }
      else {
        // We reached our target server, but it returned an error
        options.error(request.responseText);
      }
    };

    request.onerror = options.error;
    request.send();
  }

Source Code

Callbacks
Callbacks (AJAX)

See also

Please refer to the recipes about the XMLHttpRequest API and promises in this chapter to learn about more about the XMLHttpRequest API.

XMLHttpRequest (AJAX)

5.2 XMLHttpRequest (AJAX)

One of the most frequently used asynchronous APIs in the development of applications with TypeScript is the XMLHttpRequest API, which can be used to retrieve data from the server without reloading the page being displayed in the web browser.

Getting Ready

All you need to implement in this recipe is an installation of TypeScript 2.0 or higher and jQuery.

How to do it

The following code snippet can be used to request some JSON data asynchronously:

const async = true;
const url = '/my/url';
const request = new XMLHttpRequest();
request.open('GET', url, async);

request.onload = function() {
  if (request.status >= 200 && request.status < 400) {
    // Success!
    var data = JSON.parse(request.responseText);
  } else {
    // We reached our target server, but it returned an error
  }
};

request.onerror = function() {
  // There was a connection error of some sort
};

request.send();

How it works

The preceding code snippet declares three variables. The first variable is used as a configuration parameter in the AJAX request. The second variable is used to store a reference to the resource URL and the third variable is used to store a reference to the request object.

The code snippet also declares two event handlers for the onload and onerror events. The onload event handler checks the requests status and transform the response (text) into JSON if there have been no server side errors.

There’s more

Many libraries like JQuery wrap the XMLHttpRequest API to provide cross-browser support and a cleaner coding style. For example, the following code snippet performs a request identical to the one performed by the preceding code snippet using the jQuery AJAX API:

const async = true;
const url = '/my/url';

$.ajax({
  url: url,
  async : async,
  method: "GET",
  dataType : "json",
  success: function(data) {
  	// Success!
  },
  error : function() {
  	// There was a connection error of some sort
  }
}); 

Source Code

XMLHttpRequest (AJAX)
XMLHttpRequest (AJAX JQery)

See also

Please refer to the recipes about callbacks and promises in this chapter to learn about more about the XMLHttpRequest API.

Mastering asynchronous programming techniques - Introduction

In the previous chapters you learned how to automate your TypeScript development environment. We also learned how to put into practice type annotations, function, classes and other object oriented programming principles and elements. Modules were deliberately omitted because they deserve their own chapter.

  • Introduction
  • The event loop
  • AJAX
  • Callbacks
  • Arrow functions
  • Promises
  • Promise chains
  • Parallel and sequential execution of promises
  • Generators
  • Using generators to run asynchronous code in a way that looks synchronous
  • Async and await
  • Web workers

5.1 Introduction

In this chapter we are going to try and master the usage of callbacks, promises, generators, the async and await keywords, web workers and service workers. All these APIs allow us to execute a task asynchronously but, before we jump straight into the action, it is important to learn some basic concepts about the JavaScript runtime. The JavaScript runtime is extremely important when working with TypeScript because the TypeScript code that we write at design-time is just JavaScript at runtime.

The first thing that we need to know is that JavaScript runtime uses one single thread. This means that two tasks cannot be executed simultaneously and, therefore, JavaScript cannot provide us with real concurrency. The JavaScript concurrency model is based on an event loop. The event loop earned its name because its implementation can be showcased using the following code snippet:

while(queue.waitForMessage()){
  queue.processNextMessage();
}

Even though, the preceding code snippet is an extremely simplified version of the real implementation of the real world it should help us to get an idea of how it works. In order to understand how the JavaScript event loop works, we must first learn some core concepts:

  • Messages and queue: The event loop adds new messages to a queue. Each message is associated with a function. When the stack is empty, a new message is taken from the queue and processed.
  • Frames and stack: When a message is processed the function associated with the message is invoked and a new frame is added to the top of the stack. The stack is a last-in-first-out (LIFO) data structure, which contains a list of frames. If the function invokes other functions, they are added to the stack as well. As the functions returns they are removed from the stack.

    A good way to understand the stack and frames is to think about what happens when a function call itself. A frame is added to the stack with each call and after a few iterations, a stack overflow exception is thrown because the stack contains too many frames.

  • Heap: Variables and objects are allocated in the heap, which is an unstructured region of memory.

The following diagram is an abstract representation of the heap, queue, messages, stack and frames:

The runtime synchronously waits for messages to arrive and process the next message if messages are available in the queue. When an event takes place, if the event has an event handler, a new message is added to the queue.

The event loop follows a rule known as “run-to-completion”, which means that once a message is being processed, no other messages will be processed until the execution of the message being processed has finalized. A downside of this model is that if a message takes too long to complete, the application will not be able to process any other messages and will become unresponsive.

One the best characteristics of the event loop model is it never blocks. JavaScript handles I/O via events and callbacks, so when the application is waiting for an AJAX request to return, it can still process other things like user input.

Running ES6 modules with webpack

4.21 Running ES6 modules with webpack

We now understand the differences between internal and external modules and the different module formats that we can use at runtime. As you have been able to experience in this chapter dealing with modules is a problem because JavaScript has been lacking a module format that works everywhere. The good news is that that module format is about to arrive and you already know about it. I’m talking about ES6 modules. TypeScript allows us to use ES6 modules at runtime. This means that the same syntax can be used at design time and runtime. On top of that we can expect ES6 modules to work natively in all browsers and Node.js in the future. This is the main reason why you should always try to use ES6 modules.

Getting Ready

All you need to implement in this recipe is an installation of TypeScript version 2.0 or higher, Gulp and the following Gulp plugins:

  • gulp-webpack
  • gulp-typescript
  • babel-core
  • babel-loader
  • babel-preset-es2015

How to do it

First you must compile your TypeScript files into JavaScript files which use the ES6 module declaration syntax:

gulp.task('build-es6-to-es6', function () {
    const tsProject = ts.createProject('tsconfig.json', {
        typescript: require('typescript'),
        target: 'es6',
        module: 'es6'
    });

    const input = './src/module_definitions/design_time/external/es6/*.ts';
    const output = './src/module_definitions/run_time/external/es6/';

    return gulp.src(input)
        .pipe(tsProject())
        .pipe(gulp.dest(output));
});

Now that we have our JavaScript files ready we can proceed to load them. The bad news is that it is the majority of JavaScript engines have not fully implemented yet the support for ES6 modules. For that reason, we must use a module loader able to load ES6 modules. This is exactly the role of webpack in this example. SystemJS is also able to load ES6 modules but we decided to use webpack to extend the number of tools included in this chapter. We are going to use webpack as a module bundler to generate a file named bundle.js which will contain all our ES6 modules and we will be able to use in a web browser.

module.exports = {
  output : {
      filename: "bundle.js",
      // export itself to a global var
      libraryTarget: "var",
      // name of the global var: "Foo"
      library: "ndrscr"
  },
  module: {
    loaders: [
      {
        test: /\.js$/,
        loader: 'babel-loader',
        query: {
          presets: ['es2015']
        }
      }
    ]
  }
}

After configuring webpack we can create a new Gulp task to generate the bundle.js file:

gulp.task('bundle-es6-with-webpack', function () {
    const webpackConfig = require('./src/module_loaders/webpack/webpack.config.js');
    return gulp.src('./src/module_definitions/run_time/external/es6/ndrscr.js')
        .pipe(webpack(webpackConfig))
        .pipe(gulp.dest('./src/module_loaders/webpack/'));
});

Finally, we can proceed to load the bundle.js file and the app.js file from the HTML document.

<script src="../../src/module_loaders/webpack/bundle.js"></script>
<script src="../../src/module_loaders/webpack/app.js"></script>

How it works

The webpack configuration file is a CommonJS module that exports an object literal. The object literal contains the actual webpack configuration. In this case we have used the following options:

  • Filename: Allow us to set the name of the output file.
  • Library target: Allow us to decide how we want to resulting library to be used (as a global variable in this case).
  • Library: Allow us to set the name of the library at runtime (similar to Browserify standalone).
  • Loaders: Allow us to use an external plugin to be used during the load of the modules. In this case we are using Babel to compile the ES6 modules into ES5 JavaScript so we can use them in environments without ES6 modules support.
Please refer to https://webpack.js.org/concepts/configuration/ to learn more about the available webpack configuration options.

Source Code

Running ES6 modules with webpack

See also

Please refer to the recipes about ES6 modules to learn about the declaration of external modules. Refer to chapter one to learn more about Gulp.

Running SystemJS modules with SystemJS

4.20 Running SystemJS modules with SystemJS

In the preceding recipes we learned how to declare and import SystemJS modules in this recipe we will learn how to run SystemJS modules.

Getting Ready

All you need to implement in this recipe is an installation of TypeScript version 2.0 or higher, Gulp, SystemJS and the gulp-typescript plugin.

How to do it

To use SystemJS we need to import three JavaScript files:

  • The SystemJS library
  • The SystemJS configuration
  • The application’s root module So you should have something like the following in your HTML page:
    <script src="../../node_modules/systemjs/dist/system.js"></script>
    <script src="../../src/module_loaders/systemjs/system.config.js"></script>
    <script src="../../src/module_loaders/systemjs/app.js"></script>
    The following code snippet showcase a basic SystemJS configuration file:
    System.config({
    baseURL: '../../src/module_definitions/run_time/external',
    packages: {
      'systemjs': {
        map: {
          './systemjs/arrays' : 'arrays.js',
          './systemjs/collections' : 'collections.js',
          './systemjs/functions' : 'functions.js',
          './systemjs/objects' : 'objects.js',
          './systemjs/utility' : 'utility.js'
        }
      }
    }
    });
    

    How it works

    The applications root module in the companion source code looks as follows:

    System.import('systemjs/ndrscr.js').then(function(_) {
    // ...
    }
    

    The root module uses the import function to load two dependency (ndrscr). As we can see, SystemJS modules are loaded asynchronously. We didn’t have to use the full path of the ndrscr file because we configured the base URL:

    baseURL: '../../src/module_definitions/run_time/external'
    

    We also declared some mappings for some folders to ensure that SystemJS is able to find the requested modules: ```ts ‘systemjs’: { map: { ‘./systemjs/arrays’ : ‘arrays.js’, ‘./systemjs/collections’ : ‘collections.js’, ‘./systemjs/functions’ : ‘functions.js’, ‘./systemjs/objects’ : ‘objects.js’, ‘./systemjs/utility’ : ‘utility.js’ } }

```
There are many options options available but unfortunately, it is impossible to cover all of theme in this book.

Please refer to https://github.com/systemjs/systemjs/blob/master/docs/import-maps.md to learn more about the available SystemJS configuration options.

Source Code

Running SystemJS modules with SystemJS

See also

Please refer to the recipes about ES6 modules to learn about the declaration of external modules. Refer to chapter one to learn more about Gulp.

Importing SystemJS modules

4.19 Importing SystemJS modules

In the preceding recipe we learned how to declare SystemJS modules in this chapter we will learn how to import SystemJS modules.

Getting Ready

All you need to implement this recipe is an installation of TypeScript version 2.0 or higher, Gulp, SystemJS and the gulp-typescript plugin.

How to do it

We can import a AMD module using a function named import:

System.import('systemjs/ndrscr.js').then(function (_) {

    const result1 = _.arrays.first([5, 4, 3, 2, 1], 3);
    console.log(result1);

    const result2 = _.arrays.last([5, 4, 3, 2, 1], 3);
    console.log(result2); 
 
    // ...

});

How it works

The import function is defined by the SystemJS module loader and takes one argument:

  • The path or name of the module that we which to import. As we can see in the preceding code snippet, we can change the name of the modules when they are passed to the callback function. For example, we changed ndrscr to underscore (_). The import function returns a promise. A promise is an object that allow us to request a value in an asynchronous way. In this case we will try to access the imported module. If the importing is successful, the promise object will invoke the callback passed to the then method:
    System.import('systemjs/ndrscr.js').then(function(_) { //…
    

    We can also indicate a callback to be invoked if the import operation fails:

    System.import('systemjs/ndrscr.js')
    .then(function(_) { /*...*/ })
    .catch(function{ console.log(import failed!); });
    

Source Code

Importing SystemJS modules

See also

Please refer to the recipes about ES6 modules to learn about the declaration of external modules. Refer to chapter one to learn more about Gulp.