Skip to the content.

Callbacks

Callbacks allowing to utilise so called “async nature” of javascript (and typescript) by detaching current execution and continue it later.

Defined in a way that function except data parameters have also callback as last parameter.

Callback is a function with 2 parameters:

Return type of both mentioned functions is void. Because no-one is waiting for them to return value synchronous.

Callback still can use return inside, but it is used to stop its execution and exit.

import fs from "fs";
fs.readFile(
  "filename",
  (err: NodeJS.ErrnoException | null, data: Buffer): void => {
    if (err) {
      return console.error(`Error occur: ${err}`);
    }
    console.log(`Data ${data.toString()}`);
  }
);

In this example callback function will be executed “after” file descriptor is returned by OS and file contents were read out.

Function doing division, and returning an error when someone will try to divide by 0

const divide = (
  a: number,
  b: number,
  cb: (err?: Error, result?: number) => void
) => {
  try {
    if (b === 0) {
      return cb(new Error(`Cant divide by 0`));
    }
    cb(undefined, a / b); // this returns `Infinity`
  } catch (e: unknown) {
    if (e instanceof Error) cb(e);
    return cb(Error((e as any).toString()));
  }
};
const callback = (err?: Error, result?: number) => {
  if (err) {
    return console.log("error happened", err);
  }
  console.log("result", result);
};
divide(1, 1, callback); // prints: "result 1"
divide(1, 0, callback); // prints: "error happened Error: Cant divide by 0"

open code in online editor

When many callbacks are used it is easy to mess-up things and produce complex code

import { stat, readFile, writeFile } from "fs";

type Err = NodeJS.ErrnoException | null;
const fileToUpdate = "/tmp/some-file.txt";

stat(fileToUpdate, (err: Err, stats: any) => {
  if (err) {
    return console.error(`Stats error: ${err}`);
  }
  console.log(`File stats: ${stats}`);
  readFile(fileToUpdate, (errRead: Err, contents: any) => {
    if (errRead) {
      return console.error(`File reading error: ${errRead}`);
    }
    const newContents = `${contents}-updated`;
    // throw new Error(`Nobody will handle me`)
    writeFile(fileToUpdate, newContents, (errWrite: Err) => {
      if (errWrite) {
        return console.error(`File writing error: ${errRead}`);
      }
      console.log(`Write finished`);
    });
  });
});

open code in online editor

Maintaining such code is complex and easy to make mistakes

Currently unhandled errors will be just logged to stderr, if they are not given to callbacks.