Как я могу отклонить обещание, возвращенное функцией async/await?
например. Первоначально
foo(id: string): Promise<A> {
return new Promise((resolve, reject) => {
someAsyncPromise().then((value)=>resolve(200)).catch((err)=>reject(400))
});
}
Перевести на async/await
async foo(id: string): Promise<A> {
try{
await someAsyncPromise();
return 200;
} catch(error) {//here goes if someAsyncPromise() rejected}
return 400; //this will result in a resolved promise.
});
}
Итак, как я мог должным образом отвергнуть это обещание в этом случае?
Лучше всего сделать throw
a Error
обертывание значения, что приведет к отклонению обещания с Error
обертыванием значения:
} catch (error) {
throw new Error(400);
}
Вы можете также просто throw
значение, но тогда нет информации о трассировке стека:
} catch (error) {
throw 400;
}
В качестве альтернативы верните отклоненное обещание с Error
, заверяющим значение:
} catch (error) {
return Promise.reject(new Error(400));
}
(Или просто return Promise.reject(400);
, но опять же, нет никакой информации контекста.)
(В вашем случае, когда вы используете TypeScript
и foo
значение возврата Promise<A>
, вы должны использовать return Promise.reject<A>(400 /*or error*/);
)
В ситуации async
/await
это последнее, вероятно, немного семантическое неправильное совпадение, но оно работает.
Если вы выбрали Error
, который хорошо воспроизводит все, что потребляет ваш результат foo
с синтаксисом await
:
try {
await foo();
} catch (error) {
// Here, `error` would be an `Error` (with stack trace, etc.).
// Whereas if you used `throw 400`, it would just be `400`.
}
throw
лучше, чем Promise.reject()
IMO. Стоит ли throw 400
- это другой вопрос. В OP это отклоняет 400, и мы можем утверждать, что вместо этого он должен отклонить Error
.
Следует также упомянуть, что вы можете просто связать функцию catch()
после вызова вашей операции async, потому что под капотом все еще появляется обещание.
await foo().catch(error => console.log(error));
Таким образом вы можете избежать синтаксиса try/catch
, если вам это не нравится.
Вы можете создать функцию- оболочку, которая принимает обещание и возвращает массив с данными, если нет ошибки, и ошибку, если произошла ошибка.
function safePromise(promise) {
return promise.then(data => [ data ]).catch(error => [ null, error ]);
}
Используйте это так в ES7 и в асинхронной функции:
async function checkItem() {
const [ item, error ] = await safePromise(getItem(id));
if (error) { return null; } // handle error and return
return item; // no error so safe to use item
}
Лучшим способом написания асинхронной функции было бы возвращение ожидающего обещания с самого начала и последующая обработка как отклонений, так и разрешений в обратном вызове обещания, а не просто выплевывание отклоненного обещания при ошибке. Пример:
async foo(id: string): Promise<A> {
return new Promise(function(resolve, reject) {
// execute some code here
if (success) { // let say this is a boolean value from line above
return resolve(success);
} else {
return reject(error); // this can be anything, preferably an Error object to catch the stacktrace from this function
}
});
}
Затем вы просто цепляете методы на возвращенном обещании:
async function bar () {
try {
var result = await foo("someID")
// use the result here
} catch (error) {
// handle error here
}
}
bar()
Источник - этот урок:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
Это не ответ на @T.J. Бросайте один. Просто комментарий, отвечая на комментарий "И на самом деле, если исключение будет преобразовано в отклонение, я не уверен, действительно ли я беспокоился, если это ошибка. Мои причины бросать только ошибку, вероятно, не применяются."
если ваш код использует async
/await
, тогда по-прежнему рекомендуется отклонять с помощью Error
вместо 400
:
try {
await foo('a');
}
catch (e) {
// you would still want `e` to be an `Error` instead of `400`
}
У меня есть предложение правильно обрабатывать отклонения в новом подходе, не имея нескольких блоков try-catch.
import to from './to';
async foo(id: string): Promise<A> {
let err, result;
[err, result] = await to(someAsyncPromise()); // notice the to() here
if (err) {
return 400;
}
return 200;
}
Откуда должна быть импортирована функция to.ts:
export default function to(promise: Promise<any>): Promise<any> {
return promise.then(data => {
return [null, data];
}).catch(err => [err]);
}
Кредиты отправляются Диме Гроссману по следующей ссылке.
Я знаю, что это старый вопрос, но я просто наткнулся на поток, и здесь, похоже, существует связь между ошибками и отклонением, что противоречит (во многих случаях, по крайней мере) часто повторяемого совета не использовать обработку исключений для иметь дело с ожидаемыми случаями. Чтобы проиллюстрировать: если асинхронный метод пытается аутентифицировать пользователя, и аутентификация не удалась, это отклонение (один из двух ожидаемых случаев), а не ошибка (например, если API аутентификации был недоступен).
Чтобы убедиться, что я не просто раскалывал волосы, я провел тест производительности трех разных подходов к этому, используя этот код:
const iterations = 100000;
function getSwitch() {
return Math.round(Math.random()) === 1;
}
function doSomething(value) {
return 'something done to ' + value.toString();
}
let processWithThrow = function () {
if (getSwitch()) {
throw new Error('foo');
}
};
let processWithReturn = function () {
if (getSwitch()) {
return new Error('bar');
} else {
return {}
}
};
let processWithCustomObject = function () {
if (getSwitch()) {
return {type: 'rejection', message: 'quux'};
} else {
return {type: 'usable response', value: 'fnord'};
}
};
function testTryCatch(limit) {
for (let i = 0; i < limit; i++) {
try {
processWithThrow();
} catch (e) {
const dummyValue = doSomething(e);
}
}
}
function testReturnError(limit) {
for (let i = 0; i < limit; i++) {
const returnValue = processWithReturn();
if (returnValue instanceof Error) {
const dummyValue = doSomething(returnValue);
}
}
}
function testCustomObject(limit) {
for (let i = 0; i < limit; i++) {
const returnValue = processWithCustomObject();
if (returnValue.type === 'rejection') {
const dummyValue = doSomething(returnValue);
}
}
}
let start, end;
start = new Date();
testTryCatch(iterations);
end = new Date();
const interval_1 = end - start;
start = new Date();
testReturnError(iterations);
end = new Date();
const interval_2 = end - start;
start = new Date();
testCustomObject(iterations);
end = new Date();
const interval_3 = end - start;
console.log('with try/catch: ${interval_1}ms; with returned Error: ${interval_2}ms; with custom object: ${interval_3}ms');
Некоторые из вещей, которые там есть, включены из-за моей неуверенности в отношении интерпретатора Javascript (мне нравится проходить только одну кроличью нору за раз); например, я включил функцию doSomething
и назначил ее возврат dummyValue
чтобы гарантировать, что условные блоки не будут оптимизированы.
Мои результаты были:
with try/catch: 507ms; with returned Error: 260ms; with custom object: 5ms
Я знаю, что есть много случаев, когда не стоит искать небольшие оптимизации, но в более масштабных системах эти вещи могут иметь большое кумулятивное значение, и это довольно резкое сравнение.
ТАК... хотя я думаю, что принятый подход к ответу является здравым в тех случаях, когда вы ожидаете обработки непредсказуемых ошибок в рамках асинхронной функции, в случаях, когда отклонение просто означает "вам придется пойти с планом B (или C или D…) "Я думаю, что я предпочел бы отказаться от использования пользовательского объекта ответа.
Promise
конструктора антипаттерна ! Даже первый фрагмент должен был быть написанfoo(id: string): Promise<A> { return someAsyncPromise().then(()=>{ return 200; }, ()=>{ throw 400; }); }