util.promisify în Node.js

Criptografie și utilitare Node.js 8.0; callbackify din 8.2

Transformă o funcție cu callback în stil Node.js (eroare, rezultat) într-o funcție care întoarce o promisiune.

Sintaxă
import { promisify, callbackify } from "node:util";
const fAsync = promisify(f);
const rezultat = await fAsync(argumente);

Ce face util.promisify

Multe funcții vechi din Node.js și din pachete primesc ca ultim argument un callback de forma (eroare, rezultat) => {}. promisify(f) întoarce o funcție nouă, care primește aceleași argumente fără callback și întoarce o promisiune: rezolvată cu rezultatul sau respinsă cu eroarea. Așa le poți folosi cu await.

Pentru majoritatea modulelor de bază există deja variante cu promisiuni (node:fs/promises, node:timers/promises, node:stream/promises). promisify rămâne util pentru exec, scrypt, zlib.gzip și pentru pachete mai vechi.

Unele funcții au o variantă pregătită, marcată cu util.promisify.custom; promisify o folosește automat (de aceea promisify(exec) întoarce { stdout, stderr }). callbackify face invers: dintr-o funcție async face una cu callback.

Parametri

Parametru Descriere
f O funcție al cărei ultim argument este un callback (eroare, valoare).

Ce returnează

O funcție care întoarce o promisiune.

Exemple

Exemplul 1: O funcție veche cu callback

import { promisify } from "node:util";

function cautaElev(nume, callback) {
  const note = { Ana: [10, 9], Ion: [7, 8] };
  setTimeout(() => {
    if (!(nume in note)) return callback(new Error(`Elevul ${nume} nu există`));
    callback(null, note[nume]);
  }, 5);
}

const cautaElevAsync = promisify(cautaElev);

console.log(await cautaElevAsync("Ana"));
try {
  await cautaElevAsync("Maria");
} catch (eroare) {
  console.log(eroare.message);
}

Rezultat

[ 10, 9 ]
Elevul Maria nu există

Exemplul 2: Funcții din Node.js: exec și zlib

import { promisify } from "node:util";
import { exec } from "node:child_process";
import { gzip, gunzip } from "node:zlib";

const { stdout } = await promisify(exec)("echo Bună ziua");
console.log(stdout.trim());

const comprimat = await promisify(gzip)("text de comprimat");
console.log((await promisify(gunzip)(comprimat)).toString());

Rezultat

Bună ziua
text de comprimat

Exemplul 3: callbackify: invers

import { callbackify } from "node:util";

async function totalComanda(produse) {
  return produse.reduce((s, p) => s + p, 0);
}

const totalCuCallback = callbackify(totalComanda);
totalCuCallback([12, 30, 7.5], (eroare, total) => {
  console.log(eroare, total);
});

Rezultat

null 49.5

Greșeli frecvente

  • Aplici promisify pe o funcție al cărei callback nu are eroarea pe primul loc (de exemplu setTimeout(fn, ms) sau un callback (rezultat) =>): promisiunea nu se rezolvă corect.
  • Aplici promisify pe o metodă și pierzi this: promisify(obiect.metoda) apelează metoda fără obiect. Scrie promisify(obiect.metoda.bind(obiect)).
  • Folosești promisify(fs.readFile) în cod nou: există deja import { readFile } from "node:fs/promises".

Comenzi înrudite

  • child_process.exec: Rulează o comandă în shell (ca în terminal) și primește la final tot textul afișat de ea.
  • crypto.scrypt (parole): Transformă o parolă într-o cheie greu de spart, cu sare aleatorie: modul corect de a salva parolele cu modulele Node.js.
  • zlib (gzip, brotli): Comprimă și decomprimă date cu gzip, deflate sau Brotli: fișiere .gz, răspunsuri HTTP mai mici.
  • fs.readFile: Citește tot conținutul unui fișier: ca text dacă dai codificarea („utf8”), altfel ca Buffer cu octeții bruți.
  • timers/promises (setTimeout cu await): Variantele cu promisiuni ale temporizatoarelor: await setTimeout(1000) pentru o pauză, setInterval ca iterator asincron.