web-dev-qa-db-fra.com

Comment utiliser async/wait au plus haut niveau?

Je suis allé async/wait et après avoir lu plusieurs articles, j'ai décidé de tester les choses moi-même. Cependant, je n'arrive pas à comprendre pourquoi cela ne fonctionne pas:

async function main() {  
    var value = await Promise.resolve('Hey there');
    console.log('inside: ' + value);
    return value;
}

var text = main();  
console.log('outside: ' + text)

La console affiche les informations suivantes (noeud v8.6.0):

> extérieur: [objet Promise]

> dedans: hé là

Pourquoi le message de log à l'intérieur de la fonction est-il exécuté ensuite? Je pensais que async/wait avait été créé pour exécuter une exécution synchrone à l'aide de tâches asynchrones.

Existe-t-il un moyen d’utiliser la valeur renvoyée dans la fonction sans utiliser .then() après main()?

56
Felipe

Je n'arrive pas à comprendre pourquoi cela ne fonctionne pas.

Parce que main renvoie une promesse; Toutes les fonctions async le font.

Au plus haut niveau, vous devez soit:

  1. Utilisez une fonction async de niveau supérieur qui ne rejette jamais, ou

  2. Utilisez then et catch

# 1 - Fonction async de niveau supérieur qui ne rejette jamais

(async () => {
    try {
        var text = await main();
        console.log(text);
    } catch (e) {
        // Deal with the fact the chain failed
    }
})();

Notez la catch; vous devez gérer les rejets de promesses/les exceptions asynchrones, car rien d’autre ne va; vous n'avez aucun appelant à qui les transmettre. Si vous préférez, vous pouvez le faire en appelant le résultat via la fonction catch (plutôt que la syntaxe try/catch):

(async () => {
    var text = await main();
    console.log(text);
})().catch(e => {
    // Deal with the fact the chain failed
});

... qui est un peu plus concis (je l'aime pour cette raison).

# 2 - then et catch

main()
    .then(text => {
        console.log(text);
    })
    .catch(err => {
        // Deal with the fact the chain failed
    });

Le gestionnaire catch sera appelé si des erreurs se produisent dans la chaîne ou dans votre gestionnaire then. (Assurez-vous que votre gestionnaire catch ne génère pas d'erreurs, car rien n'est enregistré pour les gérer.)

Ou les deux arguments à then:

main().then(
    text => {
        console.log(text);
    },
    err => {
        // Deal with the fact the chain failed
    }
);

Encore une fois, notez que nous enregistrons un gestionnaire de rejet. Mais dans ce formulaire, assurez-vous qu'aucun de vos rappels then ne génère aucune erreur, rien n'est enregistré pour les traiter.

87
T.J. Crowder

Top-Level await est passé à l'étape 3 et répond donc à votre question Comment puis-je utiliser asynchrone/wait au niveau supérieur? consiste simplement à ajouter await l'appel à main():

async function main() {  
    var value = await Promise.resolve('Hey there');
    console.log('inside: ' + value);
    return value;
}

var text = await main();  
console.log('outside: ' + text)

Ou juste:

const text = await Promise.resolve('Hey there');
console.log('outside: ' + text)

N'oubliez pas qu'il n'est disponible que dans [email protected] .

2
lautaro.dragan

La solution actuelle à ce problème consiste à l’aborder différemment.

Votre objectif est probablement une sorte d'initialisation qui se produit généralement au niveau supérieur d'une application.

La solution consiste à garantir qu’il n’existe qu’une seule déclaration JavaScript au plus haut niveau de votre application. Si vous n'avez qu'une seule déclaration en haut de votre application, vous êtes libre d'utiliser async/wait à tout moment, où que vous soyez (sous réserve des règles de syntaxe habituelles).

En d'autres termes, encapsulez tout votre niveau supérieur dans une fonction afin que ce ne soit plus le niveau supérieur et que la question de savoir comment exécuter async/wait au niveau supérieur d'une application soit résolue, ce qui n'est pas le cas.

Voici à quoi devrait ressembler le niveau supérieur de votre application:

import {application} from './server'

application();
2
Duke Dougal

Pour donner quelques informations supplémentaires en plus des réponses actuelles:

Le contenu d'un fichier node.js est actuellement concaténé, sous forme de chaîne, pour former un corps de fonction.

Par exemple, si vous avez un fichier test.js:

// Amazing test file!
console.log('Test!');

Alors node.js concaténera secrètement une fonction qui ressemble à:

function(require, __dirname, ... a bunch more top-level properties) {
  // Amazing test file!
  console.log('test!');
}

La principale chose à noter est que la fonction résultante n'est PAS une fonction asynchrone. Vous ne pouvez donc pas utiliser le terme await directement à l'intérieur de celui-ci!

Mais supposons que vous ayez besoin de travailler avec des promesses dans ce fichier. Deux méthodes sont alors possibles:

  1. Ne pas utiliser await directement dans la fonction
  2. Ne pas utiliser await

L'option 1 nécessite la création d'une nouvelle portée (et CETTE portée peut être async, car nous en avons le contrôle):

// Amazing test file!
// Create a new async function (a new scope) and immediately call it!
(async () => {
  await new Promise(...);
  console.log('Test!');
})();

L'option 2 requiert l'utilisation de l'API de promesse orientée objet (le paradigme moins joli mais tout aussi fonctionnel de travailler avec des promesses).

// Amazing test file!
// Create some sort of promise...
let myPromise = new Promise(...);

// Now use the object-oriented API
myPromise.then(() => console.log('Test!'));

J'espère personnellement que, si cela fonctionne, node.js concaténera par défaut le code dans une fonction async. Cela permettrait de se débarrasser de ce mal de tête.

1
Gershom Maes

Étant donné que main() est exécuté de manière asynchrone, une promesse est renvoyée. Vous devez obtenir le résultat dans la méthode then(). Et comme then() retourne aussi sa promesse, vous devez appeler process.exit() pour mettre fin au programme. 

main()
   .then(
      (text) => { console.log('outside: ' + text) },
      (err)  => { console.log(err) }
   )
   .then(() => { process.exit() } )
0
Peracek