В Node.js практически все асинхронно. А главным инструментом асинхронного кода являются функции обратного вызова. В связи с этим используется особый паттерн при котором ФОВ принимает в качестве первого аргумента ошибку, а в качестве второго возможный результат операции. И все бы ничего, но если вым нужно выполнить несколько последовательных операций, то код начнет потихоньку двигаться к правой стороне экрана из-за возрастающего количества вложенных функций:
doSomething((error, data) => {
if (error) return handleError(error);
doSomethingElse((error, data) => {
if (error) return handleError(error);
doAnotherThing((error, data) => {
if (error) return handleError(error);
okayThatsEnough((error, data) => {
if (error) return handleError(error);
console.log('Done');
});
});
});
});
Скорее всего вы слышали термины Callback Hell и Pyramid of Doom, которые как раз и означают код структурированный таким образом.
Одним из решений данной проблемы являются обещания или как их еще называют промисы:
doSomething()
.then(doSomethingElse)
.then(doAnotherThing)
.then(okayThatsEnough)
.then(() => console.log('Done'))
.catch(handleError);
Обещания позволяют по-большей части избавиться от глубокой вложенности функций и в результате чего код не только становится визуально приятнее его самое главное проще понимать. Но к сожалению встроенные модули в Node до сих пор работают через ФОВ. Но это не беда, ведь мы можем сами написать функцию которая позволит нам преобразовать функцию работающую через ФОВ в функцию работающую через обещания.
Сама функция получится очень простая, занимать она будет всего несколько строчек кода, которые при желании, можно сократить и до одной, но тем не менее написание этой функции поможет на практике увидеть такое понятие в программирование как функции высшего порядка.
Реализация
fs.readdir(__dirname, (error, files) => {
if (error) throw error;
console.log(files);
});
const readdir = promisify(fs.readdir);
readdir(__dirname)
.then(files => console.log(files))
.catch(error => console.error(error));
function promisify(fn, ...args) {
return new Promise((resolve, reject) => {
return fn(...args, (error, data) => {
if (error) return reject(error);
resolve(data);
});
});
}