> Как реализовать функцию promisify в JavaScript (JavaScript)

Уровень: senior · Роль: frontend · Категория: Технические вопросы

Компании: циан

Стек: JavaScript

> Пример ответа

Короткий ответ

promisify - это функция-утилита, которая преобразует функцию с callback-стилем (например, (err, result) => {}) в функцию, возвращающую Promise. Реализация оборачивает вызов исходной функции и в колбэке либо резолвит промис с результатом, либо реджектит с ошибкой. В Node.js есть встроенный util.promisify, но для браузера или кастомных случаев пишут свою реализацию. Ключевой момент - корректно пробросить this и аргументы, а также учесть колбэки с несколькими результатами.

Подробное объяснение

Callback-стиль в JavaScript - это соглашение, где последний аргумент функции - колбэк, вызываемый с первым аргументом-ошибкой (или null) и затем результатами. promisify создаёт новую функцию, которая принимает те же аргументы, но вместо колбэка возвращает Promise.

Базовая реализация:

  • возвращаем функцию, которая принимает ...args и возвращает new Promise
  • внутри промиса вызываем исходную функцию с ...args и добавляем в конец колбэк
  • в колбэке проверяем err - если есть, вызываем reject(err), иначе resolve(result)

Важные нюансы:

  • контекст this: если исходная функция вызывается как метод объекта, нужно сохранить this через Function.prototype.call или apply
  • несколько результатов: некоторые колбэки передают более одного результата (например, fs.readdir - (err, files)), но бывают и (err, result1, result2) - тогда нужно резолвить массив или объект
  • сигнатура колбэка: не все функции следуют Node.js соглашению (первый аргумент - ошибка), поэтому иногда нужна опция для кастомного колбэка
  • обработка ошибок синхронного выброса: если исходная функция бросает исключение до вызова колбэка, его нужно поймать и отклонить промис

В Node.js util.promisify дополнительно поддерживает символ util.promisify.custom для переопределения поведения и обрабатывает колбэки с несколькими аргументами через resolve с массивом.

На практике

На собеседовании важно показать понимание не только базовой механики, но и edge cases. Обычно просят написать реализацию, затем задают вопросы про this, множественные результаты и совместимость с Node.js API.

Практические сценарии:

  • обёртка для старых API, которые используют колбэки (например, XMLHttpRequest, IndexedDB, старые библиотеки)
  • унификация асинхронного кода в проекте, где уже используются async/await
  • тестирование: promisify позволяет писать тесты на основе промисов вместо колбэков

Хорошая реализация должна быть компактной, но покрывать основные случаи. Не нужно переусложнять - интервьюер оценит ясность и внимание к деталям.

Пример кода

JAVASCRIPT
function promisify(fn) {
return function(...args) {
return new Promise((resolve, reject) => {
const callback = (err, ...results) => {
if (err) {
reject(err);
} else {
resolve(results.length > 1 ? results : results[0]);
}
};
try {
fn.call(this, ...args, callback);
} catch (error) {
reject(error);
}
});
};
}
// пример использования
const fs = require('fs');
const readFile = promisify(fs.readFile);
readFile('file.txt', 'utf8')
.then(data => console.log(data))
.catch(err => console.error(err));

Как отвечать на собеседовании

Начни с краткого определения и назначения. Затем покажи базовую реализацию на доске или в коде. Обязательно упомяни:

  • почему нужна проверка err первым аргументом
  • как обрабатывается this (через fn.call(this, ...))
  • что делать с несколькими результатами колбэка
  • отличие от встроенного util.promisify (поддержка custom символа, обработка нескольких аргументов)

Если спросят про edge cases - расскажи про синхронные исключения и про то, что колбэк может быть вызван асинхронно, но это не влияет на промис. Не углубляйся в микрооптимизации, если не просят.

Что проверяет интервьюер

  • понимание асинхронности и промисов
  • знание callback-конвенции в Node.js
  • умение работать с this и аргументами функций
  • внимание к деталям (обработка ошибок, множественные результаты)
  • способность писать чистый и лаконичный код

Типичные ошибки

  • забывают про this - если функция вызывается как метод, контекст теряется
  • не обрабатывают синхронные исключения исходной функции
  • резолвят только первый результат, игнорируя дополнительные аргументы колбэка
  • не проверяют, что колбэк вызывается ровно один раз (в реальности это редкость, но стоит упомянуть)
  • переусложняют реализацию, добавляя лишние проверки, которые не нужны для базового случая

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью