Comments 12
Для TypeScript полезно правило линтера promise-function-async. Инстинктивно стараюсь избегать вызова асинхронных функций из синхронных, в идеале then/catch остаются только в единственной точке входа.
Перепишите вашу функцию так:
function fn(obj) {
return new Promise((resolve, reject) => {
try {
const someProp = obj.someProp;
resolve(someProp);
} catch (e) {
reject(e);
}
})
}
Это как раз пример того как делать НЕ надо. Во-первых, конструктор Promise сам поймает ошибку, try-catch тут полностью лишний. Ну или же лишним можно считать new Promise
, поскольку try-catch тут и сам справится.
Во-вторых, написать async гораздо проще.
Он во что-то такое развернётся:
function fn(obj) {
return new Promise( resolve => {
const someProp = obj.someProp;
resolve(someProp);
} )
}
Перевод, это конечно круто. Но перевод ради перевода? Если хотели сделать полезное, то перевели бы и единственный комментарий к оригиналу, объясняющий, почему автор неправ.
Иногда встречаются рекомендации начинать цепочку промисов с Promise.resolve()
— как раз чтобы избежать описанной проблемы.
То есть вместо
userModel.getUserById(req.params.id)
.then(user => res.json(user))
.catch()
пишем
Promise.resolve()
.then(() => userModel.getUserById(req.params.id))
.then(user => res.json(user))
.catch()
Минус — лишний код. Плюс — работает даже в случае, когда userModel.getUserById
пришла из сторонней библиотеки, и мы не можем явно пометить ее как async
Выше уже заметили, что нужно помещать код внутрь new Promise((resolve, reject) => {...})
, чтобы избежать такой проблемы (в случае если async-await по какой-то причине использовать нельзя, например, IE11).
В общем-то именно ради возможности словить ошибку и появилось такое API c замыканием вместо классического Defferred.resolve/reject
из jQuery и других ранних реализаций промисов. Вот тут можно почитать подробнее: https://stackoverflow.com/questions/28687566/difference-between-defer-promise-and-promise
в случае если async-await по какой-то причине использовать нельзя, например, IE11
ну в IE11 и Promise нативно не поддерживается, так что реальных причин не использовать везде async-await не существует.
Полифилл promise подключается один раз и весит 1Kb или около того, а транспиляция раздувает исходный код. Было
async function loadJSON() {
const response = await fetch('/url');
const json = await response.json();
return json;
}
стало
function loadJSON() {
var response, json;
return regeneratorRuntime.async(function loadJSON$(_context) {
while (1) {
switch (_context.prev = _context.next) {
case 0:
_context.next = 2;
return regeneratorRuntime.awrap(fetch('/url'));
case 2:
response = _context.sent;
_context.next = 5;
return regeneratorRuntime.awrap(response.json());
case 5:
json = _context.sent;
return _context.abrupt("return", json);
case 7:
case "end":
return _context.stop();
}
}
});
}
Поэтому я могу вполне себе представить вариант, где Promise доступен, а async/await – нет.
Не могу утверждть, работает ли это так как ожидается, но есть плагин для babel, который преобразует async-await в Promise.
Т.е. решая проблему "в лоб" можно глобально подключить полифил + плагин для async->Promise
Интересно, хороший плагин, очень жаль, что стандартный @babel/preset-env
, производит намного больше кода.
Судя по примеру по ссылке, этот плагин ломает код. При преобразовании URL.createObjectURL(blob)
в .then(URL.createObjectURL)
у метода createObjectURL теряется контест (this). И если конкретно для этого метода контекст не требуется, для других методов это не так.
Разница между асинхронной функцией и функцией, возвращающей промис