web-dev-qa-db-ja.com

非同期関数から例外をスローする方法

失敗時に例外をスローすることを期待する非同期関数があります。しかし、何かがこれを妨げるようです:

try catchブロックを省略することにより、関数の外部で処理したい例外がスローされることを期待しています。

私が得る実際の結果はやや混乱しています:

(node:10636) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): E11000 duplicate key error index.

(node:10636) DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

async f(obj) {
    await db.collection('...').save(obj);
}

例外をキャッチして、代わりに他の何かをスローしようとすると、同じ結果が得られます。

async f(obj) {
    try {
        await db.collection('...').save(obj);
    } catch(e) {
        throw e.message;
    }
}

この関数はtryブロックから呼び出されるので、これが未処理の約束であることはわかりません。

fを別の関数のパラメーターとして使用しようとしています。

g(obj, handler) {
    try {
        handler(obj);
    } catch(e);
        ...
    }
}

g(objToSave, f);
17
user1063963

最終的に、あなたがしようとしているのは、非同期関数fを同期関数gで呼び出すことです。同期関数へ)。

代わりに、gasyncでなければならないか、fによって返されるプロミスを適切に処理する必要があります。ただし、 このコメントでfで表される関数は常にプロミスを返すとは限らないと述べています。その場合、前者のオプションが最も簡単に実装できます。

async g(obj, handler) {
  return await handler(obj);
}

これは、handlerがpromiseを返さず、単なる値(ドキュメント化されている here )でも機能します。

gを(再度)呼び出すには、非同期関数、または返されたプロミスを処理するコードのいずれかが必要です。

g(objToSave, f).then(...).catch(...)
11
robertklep
_async f(obj) {
    try {
        await db.collection('...').save(obj);
    } catch(e) {
        throw e.message;
    }
}
_

この関数はtryブロックから呼び出されるので、これが未処理の約束であることはわかりません。

ここで処理されないのは、f()メソッドではなく、.save()関数によって返されるプロミスの拒否です。したがって、これはその問題を引き起こしません:

_async f(obj) {
    try {
        await db.collection('...').save(obj);
    } catch(e) {
        console.error(e.message);
    }
}
_

非同期関数で例外をスロー常に約束を拒否する返されるその関数によって

例外をキャッチするには、別の非同期関数でこれを行う必要があります。

_try {
    asyncFunc();
} catch (err) {
    // you have the error here
}
_

または、拒否ハンドラを明示的に追加できます。

_asyncFunc().catch(err => {
    // you have the error here
});
_

例外をキャッチして別の例外をスローしている場合、異なる関数で同じ問題が発生します。

約束の拒否ハンドラーを追加して例外をスローしたり、拒否された約束をそこに返さないようにするか、同じ関数または新しい例外を再スローする代わりに、例外を処理する別の非同期関数でその関数を実行する必要があります。

まとめると:Every async関数はpromiseを返します。すべてのプロミスには拒否ハンドラが必要です。

拒否ハンドラーは、2つの関数.then()を使用して、または.catch()で、またはtry { await asyncFunction(); } catch (err) { ... }で追加されます。

拒否ハンドラーなしでプロミスを拒否すると、古いバージョンのNodeで警告が表示され、新しいバージョンのNode-を参照してください。詳細については、この回答:

16
rsp

関数の外で処理したい

それはあなたがするのを忘れた唯一のことです。 (警告が未処理拒否について文句を言っている理由です)。関数fは正常に機能しています。

async functionから同期例外をスローすることはできません。すべてが非同期であり、例外は結果プロミスの拒否につながります。それはあなたがキャッチする必要があるものです:

function g(obj, handler) {
    try {
        Promise.resolve(handler(obj)).catch(e => {
            console.error("asynchronous rejection", e);
        });
    } catch(e) {
        console.error("synchronous exception", e);
    }
}
// or just
async function g(obj, handler) {
    try {
        await handler(obj);
//      ^^^^^
    } catch(e) {
        console.error("error", e);
    }
}

g(objToSave, f);
1
Bergi