Javascript: цепочка обещаний против async/ждут?
Я узнаю о Javascript Promise
и async
/await
. Пример кода ниже асинхронно считывает и анализирует файл JSON в node.js(моя версия node.js v10.0.0).
В образце кода функция ChainReadJson и функция AwaitReadJson выполняют одно и то же, считывая и анализируя JSON файл. Разница в том, что функция ChainReadJson использует цепочку обещаний, а функция AwaitReadJson использует async/wait.
const FS = require("fs");
function ReadFile(fileName) {
return new Promise((Resolve, Reject) => {
FS.readFile(fileName, 'utf8', (error, result) => {
if (error)
Reject(error);
else
Resolve(result);
});
});
}
// function using promise chain
function ChainReadJson(fileName, CallBack) {
ReadFile(fileName)
.then(
res => JSON.parse(res),
err => {
Message(-1, err.message);
}
)
.then(
res => {
if (res !== undefined)
CallBack(fileName, res);
},
err => {
Message(-2, err.message);
}
);
}
// function using async/await
async function AwaitReadJson(fileName, CallBack) {
let res, json;
try {
res = await ReadFile(fileName);
}
catch (err) {
Message(-1, err.message);
return;
}
try {
json = JSON.parse(res);
}
catch (err) {
Message(-2, err.message);
return;
}
CallBack(fileName, json);
}
ChainReadJson('test.json', PrintJSON);
AwaitReadJson('test.json', PrintJSON);
// common functions
function PrintJSON(fileName, json) {
console.log('JSON[${fileName}]:', json);
}
function Message(n, str) {
console.log('[${n}]', str);
}
При написании кода для функции ChainReadJson с использованием цепочки обещаний мне было трудно контролировать результаты выполнения и ошибки.
Однако при написании кода для функции AwaitReadJson с использованием async/await эти трудности в основном исчезают.
Правильно ли я понимаю преимущества async/wait? Каковы недостатки async/await по сравнению с цепочкой обещаний?
(Образец кода - это модифицированная версия кода в этом ответе. Исходный код использует только цепочку обещаний и записывается, чтобы точно знать, где в цепочке произошла ошибка и какова ошибка)
Ответы
Ответ 1
Действительно, async/await
были разработаны для сокращения шаблона и упрощения написания асинхронных программ по сравнению с обратными вызовами, обещаниями и функциями генератора.
- Хотя обещания были созданы с той же целью, у них было дополнительное ограничение на работу в существующих JS-машинах, поэтому их синтаксис сложнее. Использование async/await требует относительно нового JS-движка. Возможно, не имеет значения, пишете ли вы собственное приложение node.js, но возможно, что библиотека должна быть совместима со старыми версиями node.js (и я не уверен, что вы можете перекрыть его для использования в старых браузерах без генератор).
- Поскольку async/await является более новым, он не оптимизирован. Сравнение, сделанное в прошлом году, сообщает о обещаниях Bluebird (библиотека JS, реализующая упрощенную версию обещаний), превосходящую асинхронный/ожидаемый результат в определенном контрольном ориентире. (Конечно, это не имеет значения, когда ваш прецедент делает несколько сетевых запросов.)
- Вам все равно понадобятся обещания выполнить несколько асинхронных действий параллельно (отредактируйте: если вам нужны их результаты)
Ответ 2
Хотя async/await
может быть хорошим способом очистки асинхронной логики, стоит отметить, что логика обещания может быть значительно очищена до такой степени, что она очень похожа на альтернативу async/await:
const fs = require("fs");
const util = require("util")
//Could also use the experimental "fs/promise" api from node v10
const promisifiedReadFile = util.promisify(fs.readFile);
const readFile = (fileName) => promisifiedReadFile(fileName, 'utf8');
function chainReadJson(fileName, callback) {
return readFile(fileName)
.then(json => JSON.parse(json))
.then(result => callback(null, result))
.catch(e => {
console.log("Error reading or parsing file", e.message);
callback(e)
});
}
Единственное функциональное отличие здесь в том, что все регистрации ошибок происходят в одном месте, в конце цепочки.
Можно сохранить разделенное ведение журнала для readFile и JSON.parse, но это, по общему признанию, немного сложнее. Обычно вы хотите перебросить ошибки после их обработки, так что обработчики downstream. .then
пропускаются: но если вы снова выбросите ошибку, то снова .catch
обработчики downstream .catch
, что приведет к дублированию ведения журнала, если вы не можете найти способ отфильтровать его.
Это выполнимо, но это немного боль, поэтому я оставил его из вышеприведенного кода.