web-dev-qa-db-fra.com

Traitement élégant des rejets sur la promesse Javascript attendue

Un bon modèle avec ES2017 async/wait est:

async function () {
  try {
    var result = await some_promised_value()
  } catch (err) {
    console.log(`This block would be processed in
      a reject() callback with promise patterns
      but this is far more intuitive`)
    return false // or something less obtuse
  }
  result = do_something_to_result(result)
  return result;
}

Pouvoir gérer des erreurs comme celle-là est vraiment agréable. Mais disons que je veux obtenir de manière asynchrone une valeur que je voudrais protéger contre la réaffectation (par exemple: une session de base de données), mais que je souhaite toujours utiliser le modèle async/wait (uniquement parce que je pense que c'est beaucoup plus intuitif).

Ce qui suit ne fonctionnera pas car const est limité au bloc:

async function () {
  try {
    const result = await get_session()
  } catch (err) {
    console.log(`This block should catch any
      instantiation errors.`)
    return false
  }
  // Can't get at result here because it is block scoped.
}

Bien sûr, vous pouvez exécuter le reste de la fonction dans le bloc try, mais vous risquez alors de vous faire prendre des erreurs qui devraient être laissées tomber ailleurs. (Par exemple, j’ai relevé ce défi en écrivant des tests où des erreurs telles que des échecs de test doivent se ramener à la suite de tests, mais je voulais gérer moi-même l’instanciation du pilote. à la suite de tests.)

La réponse simple est évidemment de ne pas utiliser ce modèle ici. Utilisez des promesses et des rappels à la place. Ou utilisez une variable var et évitez les blocs const et let. Mais j'aime les avantages de la protection de réaffectation et bloque le champ d'application pour d'autres considérations.

[question]: Y a-t-il un moyen d'encapsuler un bloc wait/async try/catch à chaque fonction? Semble être une solution potentielle, mais n’est pas aussi lisible que try/catch. Le fait que try/catch n'empiète pas sur la portée de la fonction et que je puisse utiliser return à l'intérieur d'un bloc try/catch me semble plus conforme à l'esprit de async/await, ce qui donne une logique plus procédurale au code asynchrone.

Idéalement, vous voudriez faire quelque chose comme const x = await y() catch (err) ou const x = await y() || fail, mais je ne peux imaginer quoi que ce soit avec un flux similaire syntaxiquement correct.

UPDATE: Comme suggéré par @ jmar777 ci-dessous, une autre alternative est:

const x = await y().catch(() => {/*handle errors here*/})

Ce qui est probablement le plus proche que j'ai trouvé de ces deux derniers exemples dans la pratique. Mais cela rompt la portée return qui bloque l'exécution en aval dans les exemples ci-dessus. Ce qui est une belle manière synchrone de gérer les choses.

Chaque solution a ses inconvénients.

J'ai hissé manuellement les variables avec let en haut du bloc de fonction en guise de solution de travail (comme décrit dans la réponse de jmar777), une question intéressante pour voir les différentes approches, je vais donc laisser la question ouverte pour le moment.

13
Tim Hope

Revenant à cette question quelques années plus tard, une réponse beaucoup plus simple m’a été donnée, je ne vois pas pourquoi j’étais intéressée par un petit couple try/catch et que j’exécutais plus de logique en dehors lorsque je pouvais tout exécuter. la logique et retourne à l'intérieur de l'instruction try.

En prenant l'exemple de la question et en appliquant cette structure, ce serait:

 async function () {
  try {
    const result = await get_session()
    // Do what needs to be done and then:
    return result
  } catch (err) {
    console.log(`This block should catch any
      instantiation errors.`)
    return false
  }
}

Si vous devez accéder à quelque chose du bloc try du bloc catch, relevez-le en erreur. Il ya peut-être une raison pour laquelle je devais sortir du champ d’essai lorsque j’ai posé cette question, mais je ne vois pas de scénario qui obligerait à regarder en arrière. Cette solution ne fonctionnerait pas si vous utilisiez try/catch/finally et revenez enfin. Mais je ne pense pas avoir rencontré ce modèle à l'état sauvage. 

1
Tim Hope

Vous pouvez avoir un malentendu concernant les avantages de l’utilisation de const. L'affectation d'une valeur à l'aide d'une déclaration const ne rend pas cette valeur immuable, cela signifie simplement que la valeur de cette constante ne peut pas être modifiée ni redéclarée:

La déclaration const crée une référence en lecture seule à une valeur. Cela ne signifie pas que la valeur qu'il détient est immuable, mais simplement que l'identificateur de variable ne peut pas être réaffecté. Par exemple, si le contenu est un objet, cela signifie que l'objet lui-même peut toujours être modifié. ( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/const )

Dans votre exemple, il est probablement préférable de lever manuellement votre liaison result en dehors de try/catch:

async function () {
  let result;

  try {
    result = await get_session()
  } catch (err) {
    console.log(`This block should catch any
      instantiation errors.`)
  }

  // do whatever else you need with result here...
}

Une alternative serait de restructurer le code de sorte que vous n'ayez pas besoin de vous baser sur un essai/attrapage. Par exemple:

async function () {
  const result = await get_session().catch(someRejectionHandler); 

  // do whatever else you need with result here...
}

Sachez simplement que votre code en aval doit gérer avec élégance le cas où get_session() a été rejeté et que result n'est pas initialisé en fonction d'une réponse réussie. Ce n'est pas différent de votre exemple initial, mais peut-être pas aussi évident lors de la numérisation du code.

9
jmar777