Concurrency

Prefer promises vs callbacks

Callbacks aren't clean, and they cause excessive amounts of nesting (the callback hell).
There are utilities that transform existing functions using the callback style to a version that returns promises (for Node.js see util.promisify, for general purpose see pify, es6-promisify)

Bad:

import { get } from 'request';
import { writeFile } from 'fs';

function downloadPage(url: string, saveTo: string, callback: (error: Error, content?: string) => void) {
  get(url, (error, response) => {
    if (error) {
      callback(error);
    } else {
      writeFile(saveTo, response.body, (error) => {
        if (error) {
          callback(error);
        } else {
          callback(null, response.body);
        }
      });
    }
  });
}

downloadPage('https://en.wikipedia.org/wiki/Robert_Cecil_Martin', 'article.html', (error, content) => {
  if (error) {
    console.error(error);
  } else {
    console.log(content);
  }
});

Good:

import { get } from 'request';
import { writeFile } from 'fs';
import { promisify } from 'util';

const write = promisify(writeFile);

function downloadPage(url: string, saveTo: string): Promise<string> {
  return get(url)
    .then(response => write(saveTo, response));
}

downloadPage('https://en.wikipedia.org/wiki/Robert_Cecil_Martin', 'article.html')
  .then(content => console.log(content))
  .catch(error => console.error(error));

Promises supports a few helper methods that help make code more concise:

Pattern Description
Promise.resolve(value) Convert a value into a resolved promise.
Promise.reject(error) Convert an error into a rejected promise.
Promise.all(promises) Returns a new promise which is fulfilled with an array of fulfillment values for the passed promises or rejects with the reason of the first promise that rejects.
Promise.race(promises) Returns a new promise which is fulfilled/rejected with the result/error of the first settled promise from the array of passed promises.

Promise.all is especially useful when there is a need to run tasks in parallel. Promise.race makes it easier to implement things like timeouts for promises.

Async/Await are even cleaner than Promises

With async/await syntax you can write code that is far cleaner and more understandable than chained promises. Within a function prefixed with async keyword, you have a way to tell the JavaScript runtime to pause the execution of code on the await keyword (when used on a promise).

Bad:

import { get } from 'request';
import { writeFile } from 'fs';
import { promisify } from 'util';

const write = util.promisify(writeFile);

function downloadPage(url: string, saveTo: string): Promise<string> {
  return get(url).then(response => write(saveTo, response));
}

downloadPage('https://en.wikipedia.org/wiki/Robert_Cecil_Martin', 'article.html')
  .then(content => console.log(content))
  .catch(error => console.error(error));

Good:

import { get } from 'request';
import { writeFile } from 'fs';
import { promisify } from 'util';

const write = promisify(writeFile);

async function downloadPage(url: string): Promise<string> {
  const response = await get(url);
  return response;
}

// somewhere in an async function
try {
  const content = await downloadPage('https://en.wikipedia.org/wiki/Robert_Cecil_Martin');
  await write('article.html', content);
  console.log(content);
} catch (error) {
  console.error(error);
}

results matching ""

    No results matching ""