блоки try / catch с async / await
я копаюсь в узле 7 async / await и продолжаю натыкаться на такой код
async function main() {
try {
var quote = await getQuote();
console.log(quote);
} catch(error) {
console.error(error);
}
}
Кажется, это единственная возможность разрешить / отклонить или вернуть / бросить с async/await, однако v8 не оптимизирует код в блоках try / catch?!
есть ли альтернативы?
5 ответов
варианты
альтернатива это:
async function main() {
try {
var quote = await getQuote();
console.log(quote);
} catch (error) {
console.error(error);
}
}
было бы что-то вроде этого, используя обещания явно:
function main() {
getQuote().then((quote) => {
console.log(quote);
}).catch((error) => {
console.error(error);
});
}
или что-то вроде этого, используя продолжение прохождения стиле:
function main() {
getQuote((error, quote) => {
if (error) {
console.error(error);
} else {
console.log(quote);
}
});
}
оригинальный пример
ваш исходный код приостанавливает выполнение и ждет обещания, возвращенного getQuote()
для урегулирования. Затем он продолжает выполнение и записывает возвращаемое значение var quote
и затем печатает его, если обещание было разрешено, или выдает исключение и запускает блок catch, который печатает ошибку, если обещание было отклонено.
вы можете сделать то же самое, используя API Promise напрямую, как во втором примере.
производительность
теперь, для исполнения. Давайте проверим!
я только что написал этот код -f1()
дает 1
в качестве возвращаемого значения, f2()
закидываем 1
исключение:
function f1() {
return 1;
}
function f2() {
throw 1;
}
теперь назовем один и тот же код миллион раз, сначала с f1()
:
var sum = 0;
for (var i = 0; i < 1e6; i++) {
try {
sum += f1();
} catch (e) {
sum += e;
}
}
console.log(sum);
а потом сменим f1()
to f2()
:
var sum = 0;
for (var i = 0; i < 1e6; i++) {
try {
sum += f2();
} catch (e) {
sum += e;
}
}
console.log(sum);
это результат, который я получил для f1
:
$ time node throw-test.js
1000000
real 0m0.073s
user 0m0.070s
sys 0m0.004s
это то, что я получил для f2
:
$ time node throw-test.js
1000000
real 0m0.632s
user 0m0.629s
sys 0m0.004s
кажется, что вы можете сделать что-то вроде 2 миллионов бросков в секунду в одном однопоточном процессе. Если вы делаете больше, чем это, то вам, возможно, придется беспокоиться о он.
резюме
я бы не беспокоился о таких вещах в Node. Если такие вещи часто используются, то в конечном итоге они будут оптимизированы командами V8 или SpiderMonkey или Chakra, и все будут следовать - это не похоже на то, что это не оптимизировано в принципе, это просто не проблема.
даже если он не оптимизирован, я все равно буду утверждать, что если вы максимизируете свой процессор в узле, то вы, вероятно, должны написать свой номер, хрустящий в C - вот что туземные аддоны, среди прочего, для. Или, может быть, такие вещи, как узел.родной было бы лучше подходит для работы, чем узел.js.
мне интересно, что было бы прецедентом, который должен бросать так много исключений. Обычно выбрасывание исключения вместо возврата значения является, ну, исключением.
Альтернатива, Похожая На Обработку Ошибок В Golang
поскольку async / await использует обещания под капотом, вы можете написать небольшую функцию утилиты следующим образом:
export function catchEm(promise) {
return promise.then(data => [null, data])
.catch(err => [err]);
}
затем импортируйте его, когда вам нужно поймать некоторые ошибки, и оберните свою асинхронную функцию, которая возвращает обещание с ней.
import catchEm from 'utility';
async performAsyncWork() {
const [err, data] = await catchEm(asyncFunction(arg1, arg2));
if (err) {
// handle errors
} else {
// use data
}
}
async function main() {
var getQuoteError
var quote = await getQuote().catch(err => { getQuoteError = err }
if (getQuoteError) return console.error(err)
console.log(quote)
}
альтернативно вместо объявления возможного var для удержания ошибки в верхней части вы можете сделать
if (quote instanceOf Error) ...
хотя это не будет работать, если что-то вроде ошибки TypeError или ссылки будет брошено. Вы можете убедиться, что это обычная ошибка, хотя с
async function main() {
var quote = await getQuote().catch(err => {
console.error(err)
return new Error('Error getting quote')
})
if (quote instanceOf Error) return quote // get out of here or do whatever
console.log(quote)
}
мое предпочтение для этого-обернуть все в большой блок try-catch, где создается несколько обещаний, может сделать его громоздким для обработки ошибки специально для обещания, которое создало он. С альтернативой несколько блоков try-catch, которые я нахожу одинаково громоздкими
альтернативой блоку try-catch является await-to-js lib. Я часто им пользуюсь. Например:
import to from 'await-to-js';
async function main(callback) {
const [err,quote] = await to(getQuote());
if(err || !quote) return callback(new Error('No Quote found');
callback(null,quote);
}
этот синтаксис намного чище по сравнению с try-catch.
Я бы хотел сделать так:)
const sthError = () => Promise.reject('sth error');
const test = opts => {
return (async () => {
// do sth
await sthError();
return 'ok';
})().catch(err => {
console.error(err); // error will be catched there
});
};
test().then(ret => {
console.log(ret);
});
Это похоже на обработку ошибок с co
const test = opts => {
return co(function*() {
// do sth
yield sthError();
return 'ok';
}).catch(err => {
console.error(err);
});
};