All posts

Async Javascript: tricks with Promises (part one)

📅 Created 6 years ago → updated after 6 years

🏷️ #js #promises #async #archive

🔗 Promises (part two): the legend of the lost Promise

Instead of placing callbacks in your callbacks to be able callback whilst you callback, Promises allow us to write the code that resembles sync one (at least in vertical manner, contrary to the one pictured above, the callback hell):

Promise.resolve(5)
  .then(x => x + 1)
  .then(console.log)
  .catch(console.error);

How to get a Promise?

First way is using the new Promise() constructor. The async action (involving a callback) is run inside the constructor. The reslove() and reject() hooks are called inside the callback:

const promise = new Promise((resolve, reject) => {
  // callback obeys NodeJS convention
  asyncAction((err, result) => {
    if (err) reject(err)
    else resolve(result)
  })
})

promise
  .then(console.info)
  .catch(console.error)

Second way uses a couple of static methods:

const resolved = Promise.resolve(5);
const rejected = Promise.reject("ERR_REJECTED");

resolved
  .then(console.info)
  .catch(console.error); // prints "5" as info message

rejected
  .then(console.info)
  .catch(console.error); // prints "ERR_REJECTED" as error message

Third way is an evolved first one and can be used only if a callback respects the NodeJS callback style (1st argument is an error object or null if succeeded, 2nd argument delivers the result if any):

/**
 * @param {Function} asyncHandler (...arguments, callback(err, results))
 */
function promisify(asyncHandler) {
  return (...args) => new Promise((resolve, reject) => {
    asyncHandler.call(
      null,
      (err, res) => {
        if (err) reject(err)
        else resolve(res)
      },
      ...args
    )
  })
}

const readFile = require("fs").readFile
const promiseReadFile = promisify(readfile)

promiseReadFile("./example.txt")
  .then(console.info)
  .catch(console.error)

Lucky us programming nowadays: the promisify() is included to NodeJS utils since ages v8.0. If you're curious what's under the hood, check this out.

Mind also that NodeJS modules provide callback methods alongside with promisified ones:

import * as fsCb from "node:fs"
import * as fsProm from "node:fs/promises"

fsCb.stat("file.txt", (err, stat) => {
  if (err) console.error("Error", err)
  else console.info(stat)
})

// ——— vs ———

fsProm.stat("file.txt")
  .then(stat => console.info(stat))
  .catch(e => console.error("Error", e))

Wait, still Promises in 2024?

If you think that traditional Promise is obsolete in the async/await world, then hold on. Every async function returns Promise do its result can be treated as one:

async function delayed() {
  // do some await
  return 42
}

delayed()
  .then(res => console.info(res))
  .catch(e => console.error(e))

Next time we continue with chaining promises and building the data flow that resembles one from real world.

Promises (part two): the legend of the lost Promise