私は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ブロック内のコードを最適化しませんか。
代替案はありますか?
これに代わるもの:
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よりもジョブに適しています。
私は、非常に多くの例外をスローする必要があるユースケースは何だろうと思います。通常、値を返す代わりに例外をスローするのは、例外です。
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と比べてはるかにきれいです。
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ブロックであるため、同じように面倒です。
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
}
}
よりクリーンな代替手段は次のとおりです。
すべての非同期関数は技術的には約束であるという事実のために
関数を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を使用したりするよりも、それを呼び出す関数で処理した方がよいかもしれません。
こうしたいのですが:)
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);
});
};
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()
);