web-dev-qa-db-fra.com

Comment lever l'exception de la fonction asynchrone

J'ai une fonction asynchrone que je m'attends à lever exception en cas d'échec. Cependant, quelque chose semble empêcher cela:

en omettant les blocs try catch, je m'attends à ce qu'une exception soit levée que je veux gérer en dehors de la fonction.

Le résultat réel que j'obtiens est quelque peu déroutant:

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

J'obtiens le même résultat lorsque j'essaie d'attraper l'exception et de lancer autre chose à la place:

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

La fonction est appelée à partir d'un bloc try, alors ne voyez pas en quoi il s'agit d'une promesse non gérée.

J'essaie d'utiliser f comme paramètre d'une autre fonction:

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

g(objToSave, f);
17
user1063963

En fin de compte, ce que vous essayez de faire, c'est d'appeler une fonction asynchrone f dans une fonction synchrone g, ce qui ne fonctionnera pas (ce serait l'équivalent de pouvoir transformer une fonction asynchrone en fonction synchrone).

Au lieu de cela, g doit également être async, ou il doit gérer correctement la promesse renvoyée par f. Cependant, dans ce commentaire vous déclarez que la fonction représentée par f peut ne pas toujours renvoyer une promesse, auquel cas la première option serait la plus simple à implémenter:

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

Cela fonctionnera également si handler ne retourne pas de promesse, mais juste une valeur (qui est documentée ici ).

L'appel de g (à nouveau) nécessite soit une fonction asynchrone, soit du code pour gérer sa promesse retournée:

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

La fonction est appelée à partir d'un bloc try, alors ne voyez pas en quoi il s'agit d'une promesse non gérée.

Ce qui n'est pas géré ici, c'est le rejet d'une promesse renvoyée par la fonction f(), et non par la méthode .save(). Donc, cela ne causera pas ce problème:

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

Lancer une exception dans la fonction asynchrone rejette toujours la promesse qui est retournée par cette fonction .

Pour intercepter l'exception, vous devez soit le faire dans une autre fonction asynchrone:

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

ou vous pouvez ajouter explicitement un gestionnaire de rejet:

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

Si vous interceptez l'exception et lancez une autre exception, vous obtenez le même problème, juste dans une fonction différente.

Vous devez soit ajouter un gestionnaire de rejet de promesse et ne pas lever d'exception ou renvoyer une promesse rejetée là-bas - ou exécuter cette fonction dans une autre fonction asynchrone qui gère l'exception au lieu de renvoyer la même exception ou une nouvelle exception.

Pour résumer: Chaque fonction async renvoie une promesse. Chaque promesse doit avoir un gestionnaire de rejet.

Le gestionnaire de rejet est ajouté à l'aide d'une fonction à deux fonctions .then() ou avec .catch(), ou avec try { await asyncFunction(); } catch (err) { ... }

Lorsque vous avez un rejet de promesse sans gestionnaire de rejet, vous obtiendrez un avertissement dans les anciennes versions de Node et une erreur fatale dans les versions plus récentes de Node - voir cette réponse pour plus de détails:

16
rsp

que je veux gérer en dehors de la fonction

C'est la seule chose que tu as oublié de faire. (C'est pourquoi l'avertissement se plaint d'un refus non géré). Votre fonction f fonctionne bien.

Vous ne pouvez pas lever d'exception synchrone à partir d'un async function, tout est asynchrone et les exceptions entraîneront un rejet de la promesse de résultat. C'est ce que vous devrez attraper:

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