Как реализовать функцию promisify в JavaScript
Что проверяют
Понимание соглашения error-first колбэков, конструктора Promise и контекста this. Следом спрашивают, как использовать результат с async/await, что будет при синхронном исключении и при двойном вызове колбэка.
Как отвечать
Проговорите контракт: исходная функция принимает колбэк последним аргументом, и он вызывается как (err, value).
function promisify(fn) {
return function (...args) {
return new Promise((resolve, reject) => {
fn.call(this, ...args, (err, value) => {
if (err) reject(err);
else resolve(value);
});
});
};
}Внешняя обёртка это обычная функция, чтобы получить this вызывающего кода, а стрелка внутри этот this сохраняет. Дальше назовите поведение на краях:
- синхронная ошибка: исключение внутри executor само отклоняет промис, отдельный
try/catchне нужен; - повторный вызов колбэка: промис меняет состояние один раз, следующие
resolveиrejectигнорируются; - несколько значений:
util.promisifyиз Node отдаёт только первое. Можно собирать(err, ...values)в массив, но это уже другой контракт; - своя промис-версия: в Node функция может отдать её через символ
util.promisify.custom.
Для фронтенда добавьте контекст. Большинство браузерных API уже возвращают промисы, а колбэчные (setTimeout, FileReader, запросы IndexedDB) не следуют схеме error-first, поэтому их оборачивают вручную: const sleep = (ms) => new Promise((r) => setTimeout(r, ms)). В Node для файлов давно есть fs/promises, оборачивать fs.readFile уже не требуется.
Где спотыкаются
Вызывают fn(...args, cb) без контекста. Для отдельной функции это незаметно, а метод объекта вроде client.get падает, потому что внутри this стал undefined.
Применяют promisify к функции, у которой колбэк не последний или не error-first. С setTimeout промис либо не разрешится никогда, либо первый аргумент результата уйдёт в reject как ошибка.
Оборачивают в new Promise то, что уже возвращает промис. Получается лишний слой, в котором легко потерять отклонение: ошибка внутреннего промиса не доходит до внешнего, и вызывающий код ждёт вечно.