try / catch blocks with async / wait

Zagłębiam się w funkcję node 7 async/wait I ciągle natkam się na kod w ten sposób

async function main() {
  try {
    var quote = await getQuote();
    console.log(quote);
  } catch(error) {
    console.error(error);
  }
}

Wydaje się, że jest to jedyna możliwość resolve/reject lub return/throw with async/wait, jednak v8 nie optymalizuje kodu w blokach try / catch?!

Czy są jakieś alternatywy?

Author: Felix Kling, 2016-11-30

5 answers

Alternatywy

Alternatywa dla tego:

async function main() {
  try {
    var quote = await getQuote();
    console.log(quote);
  } catch (error) {
    console.error(error);
  }
}

Byłoby coś takiego, używając obietnic wprost:

function main() {
  getQuote().then((quote) => {
    console.log(quote);
  }).catch((error) => {
    console.error(error);
  });
}

Lub coś w tym stylu, używając stylu przejścia kontynuacyjnego:

function main() {
  getQuote((error, quote) => {
    if (error) {
      console.error(error);
    } else {
      console.log(quote);
    }
  });
}

Oryginalny przykład

Oryginalny kod powoduje zawieszenie wykonania i oczekiwanie na rozliczenie obietnicy zwróconej przez getQuote(). Następnie kontynuuje wykonywanie i zapisuje zwróconą wartość do var quote, a następnie wypisuje ją, jeśli obietnica została rozwiązana, lub rzuca wyjątek i uruchamia blok catch, który wyświetla błąd, jeśli obietnica została odrzucona.

Możesz zrobić to samo używając API Promise bezpośrednio, jak w drugim przykładzie.

Wydajność

A teraz przedstawienie. Przetestujmy to!

Właśnie napisałem ten kod - f1() daje 1 jako wartość zwracaną, f2() rzuca 1 jako wyjątek:

function f1() {
  return 1;
}

function f2() {
  throw 1;
}

Teraz nazwijmy ten sam kod milion razy, najpierw f1():

var sum = 0;
for (var i = 0; i < 1e6; i++) {
  try {
    sum += f1();
  } catch (e) {
    sum += e;
  }
}
console.log(sum);

I więc zmieńmy f1() na f2():

var sum = 0;
for (var i = 0; i < 1e6; i++) {
  try {
    sum += f2();
  } catch (e) {
    sum += e;
  }
}
console.log(sum);

Oto wynik, który otrzymałem za f1:

$ time node throw-test.js 
1000000

real    0m0.073s
user    0m0.070s
sys     0m0.004s

To jest to, co mam dla f2:

$ time node throw-test.js 
1000000

real    0m0.632s
user    0m0.629s
sys     0m0.004s

Wydaje się, że możesz zrobić coś w rodzaju 2 milionów rzutów na sekundę w jednym procesie jednowątkowym. Jeśli robisz więcej niż to, może trzeba się tym martwić.

Podsumowanie

Nie martwiłbym się o takie rzeczy w Node. Jeśli takie rzeczy się dużo zużywają, to w końcu zostanie zoptymalizowany przez V8 lub Spidermonkey lub zespoły Chakra i każdy będzie śledzić - to nie jest tak, że nie jest zoptymalizowany jako zasada, to po prostu nie jest problemem.

Nawet jeśli nie jest zoptymalizowany to i tak argumentowałbym, że jeśli masz maksymalnie CPU w Node to powinieneś chyba napisać swój numer w C - po to są między innymi natywne dodatki. A może coś jak węzeł.natywny byłby lepiej dostosowany do tego zadania niż węzeł.js.

Zastanawiam się, jaki byłby przypadek użycia to wymaga wielu WYJĄTKÓW. Zwykle rzucenie wyjątku zamiast zwracania wartości jest, cóż, wyjątkiem.

 63
Author: rsp,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-27 20:31:19

Alternatywa Podobna Do Obsługi Błędów W Golang

Ponieważ async / wait używa obietnic pod maską, możesz napisać taką małą funkcję użyteczności:

export function catchEm(promise) {
  return promise.then(data => [null, data])
    .catch(err => [err]);
}

Następnie zaimportuj ją, gdy chcesz wyłapać błędy, i zawiń funkcję asynchroniczną, która zwraca obietnicę.

import catchEm from 'utility';

async performAsyncWork() {
  const [err, data] = await catchEm(asyncFunction(arg1, arg2));
  if (err) {
    // handle errors
  } else {
    // use data
  }
}
 8
Author: Steve Banton,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-12 00:12:56
async function main() {
  var getQuoteError
  var quote = await getQuote().catch(err => { getQuoteError = err }

  if (getQuoteError) return console.error(err)

  console.log(quote)
}

Alternatywnie zamiast deklarowania możliwego var, aby utrzymać błąd na górze, możesz zrobić

if (quote instanceOf Error) ...

Chociaż to nie zadziała, jeśli zostanie wyrzucony błąd TypeError lub reference error. Możesz upewnić się, że jest to zwykły błąd za pomocą

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)
}

Moim preferowaniem jest owijanie wszystkiego w duży blok try-catch, w którym powstaje wiele obietnic, które mogą sprawić, że kłopotliwe będzie obchodzenie się z błędem specjalnie do obietnicy, która go stworzyła. Z alternatywą bycie wieloma blokami try-catch, które uważam za równie uciążliwe

 6
Author: Tony,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-30 17:37:09

Alternatywą dla bloku try-catch jest wait-to-js lib. Często go używam. Na przykład:

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);

}

Ta składnia jest znacznie czystsza w porównaniu do try-catch.

 6
Author: Pulkit chadha,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-02-08 08:39:56

Chciałbym zrobić tak:)

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);
});

Jest to podobne do obsługi błędów za pomocą co

const test = opts => {
  return co(function*() {

    // do sth
    yield sthError();
    return 'ok';

  }).catch(err => {
    console.error(err);
  });
};
 1
Author: Cooper Hsiung,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-12 09:01:51