web-dev-qa-db-ja.com

async / awaitでブロックを試してみてください。

私はnode 7のasync/await機能を詳しく調べていて、このようなコードに悩まされ続けています

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

これはasync/awaitで解決/拒否またはreturn/throwする唯一の可能性があるようですが、v8はtry/catchブロック内のコードを最適化しませんか。

代替案はありますか?

83
Patrick

代替案

これに代わるもの:

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に書き込み、その後、promiseが解決された場合はそれを出力します。またはpromiseが拒否された場合はエラーを出力するcatchブロックを実行します。

2番目の例のように、Promise APIを直接使用して同じことを実行できます。

パフォーマンス

今、パフォーマンスのために。テストしましょう。

私はちょうどこのコードを書いた - 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()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

1つのシングルスレッドプロセスで200万秒が1秒間にスローされるようなことができるようです。それ以上のことをしているのであれば、心配する必要があるかもしれません。

概要

Nodeのようなことは心配しないでください。このようなことが頻繁に使われるようになると、それはV8、SpiderMonkey、Chakraの各チームによって最終的に最適化され、全員が従うようになります - 原則として最適化されていないわけではなく、問題ではありません。

たとえ最適化されていなくても、NodeでCPUを使い果たしているのであれば、Cで数値を整理する必要があるでしょう - それがネイティブアドオンのためのものです。あるいは、 node.native のようなものがNode.jsよりもジョブに適しています。

私は、非常に多くの例外をスローする必要があるユースケースは何だろうと思います。通常、値を返す代わりに例外をスローするのは、例外です。

95
rsp

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と比べてはるかにきれいです。

12
Pulkit chadha
async function main() {
  var getQuoteError
  var quote = await getQuote().catch(err => { getQuoteError = err }

  if (getQuoteError) return console.error(err)

  console.log(quote)
}

代わりに、先頭にエラーを保持するために可能な変数を宣言する代わりに、あなたはすることができます

if (quote instanceof Error) {
  // ...
}

TypeErrorやReferenceエラーのようなものが投げられてもそれはうまくいきませんが。あなたはそれがで定期的なエラーであることを確認することができます

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ブロックであるため、同じように面倒です。

11
Tony

Golangのエラー処理に似た代替手段

Async/awaitは内部で約束を使用するので、このような小さな効用関数を書くことができます。

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

それから、エラーを捕捉する必要があるときはいつでもそれをインポートしてください、そしてそれと共に約束を返すasync関数をラップしてください。

import catchEm from 'utility';

async performAsyncWork() {
  const [err, data] = await catchEm(asyncFunction(arg1, arg2));
  if (err) {
    // handle errors
  } else {
    // use data
  }
}
10
Steve Banton

よりクリーンな代替手段は次のとおりです。

すべての非同期関数は技術的には約束であるという事実のために

関数をawaitで呼び出すときに、キャッチを関数に追加できます。

async function a(){
    let error;

    // log the error on the parent
    await b().catch((err)=>console.log('b.failed'))

    // change an error variable
    await c().catch((err)=>{error=true; console.log(err)})

    // return whatever you want
    return error ? d() : null;
}
a().catch(()=>console.log('main program failed'))

すべてのpromiseエラーが処理されるため、try catchの必要はなく、コードエラーはないので、親ではそれを省略できます!!

Mongodbで作業しているとしましょう。エラーがある場合は、ラッパーを作成したり、try catchを使用したりするよりも、それを呼び出す関数で処理した方がよいかもしれません。

4
zardilior

こうしたいのですが:)

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);
  });
};
1
Cooper Hsiung

catchこの方法でのingは、私の経験では危険です。このプロミスからのエラーだけでなく、スタック全体でスローされたエラーはすべてキャッチされます(おそらく、これは望んでいないことです)。

Promiseの2番目の引数は、すでに拒否/失敗のコールバックです。代わりにそれを使用する方がより安全です。

これを処理するために書いたTypeScriptタイプセーフワンライナーは次のとおりです。

function wait<R, E>(promise: Promise<R>): [R | null, E | null] {
  return (promise.then((data: R) => [data, null], (err: E) => [null, err]) as any) as [R, E];
}

// Usage
const [currUser, currUserError] = await wait<GetCurrentUser_user, GetCurrentUser_errors>(
  apiClient.getCurrentUser()
);
0
sarink