8
votes

wait n'est valide que dans la fonction async - eval dans async

Je veux eval () quelques lignes de code à l'intérieur de la fonction async. Bien que le code suivant soit correct,

let ctxScript = 'await foo1(); await foo2();';
async function foo()
{
  eval( ctxScript );
}

ce qui suit génère une erreur: wait n'est valide que dans la fonction asynchrone

async function foo()
{
  await foo1();
  await foo2();
}

Comment pourrais-je gérer ça? Mon toto () doit être asynchrone car il s'agit de la fonction de contrôleur de marionnettiste


7 commentaires

Prenez du recul - pourquoi voulez-vous eval le code? Si vous nous donnez le vrai problème, peut-être qu'il y a une solution différente


Je souhaite exécuter différentes actions en raison de conditions différentes. Mon async foo () est gros, mais seuls les petits morceaux de code que je veux exécuter par eval () sont différents.


Vous pouvez déjà exécuter différentes fonctions sans avoir besoin d' eval - if par exemple, ou appeler une fonction complètement différente, polymorphisme, configuration de tables de recherche avec des fonctionnalités, etc.


Ok, mon problème X est que je veux utiliser un code différent à l'intérieur de la fonction, qui devrait être asynchrone, quel est le meilleur Y pour le faire? Déplacez tout le code async () dans includes, comme indiqué dans la réponse n ° 1?


Le passer comme un rappel?


Passer quoi et où comme rappel? Vraiment, j'ai un gros contenu de fonction async (), et quelque part au milieu de celui-ci, j'ai besoin d'exécuter différents scripts, sans perdre de contexte. J'ai décidé de faire eval () inclut des scripts, que je charge par fs.readFileSync avant, s'il y a une meilleure façon de gérer cela, veuillez me donner un exemple. Il s'agit d'une fonction de contrôleur Puppetteer, et j'ai besoin de tester des centaines de ces scripts, choisis dynamiquement, qui utilisent différents sélecteurs.


Pourquoi faut-il toujours que quelqu'un dise de ne pas utiliser eval quand on pose des questions sur eval? Répondez simplement à la question, il existe des cas d'utilisation légitimes pour cela.


4 Réponses :


13
votes

foo() ne doit pas nécessairement être async , car cela n'a aucun effet sur le contexte d'exécution de eval . Au lieu de cela, une solution possible consiste à envelopper votre ctxScript dans une fonction asynchrone auto-exécutable, comme ceci: eval("(async () => {" + ctxScript + "})()")


6 commentaires

Mon toto () doit être asynchrone car il s'agit de la fonction de contrôleur Puppetteer.


Mon point était que le fait que foo () soit asynchrone ou non n'a aucune incidence sur le fait que le code à l'intérieur de l'évaluation soit dans un contexte d'exécution asynchrone ou non. D'où ma réponse.


Donc oui, await ne fonctionne pas dans eval (), dans la fonction async.


D'après les commentaires ci-dessus, utiliser eval ici n'est peut-être pas la meilleure solution à votre problème, car il présente d'énormes inconvénients (plus lent, contexte d'exécution différent, risque d'injection de code).


Je vois, eval () là pour le moment uniquement comme un patch rapide, voyant et comprenant et acceptant tous les risques. Mais c'est ma première fois avec node.js et async, donc je ne connais pas encore de meilleures façons.


@RandyVogel oui, c'est important car maintenant cette instruction eval renvoie une promesse, au lieu de la valeur de la promesse remplie.



1
votes

Si vous souhaitez appeler dynamiquement du code asynchrone dans une fonction plus grande, vous pouvez fournir un rappel qui le fait pour vous. De cette façon, vous pouvez appeler votre fonction avec différentes fonctionnalités supplémentaires en lui donnant différentes fonctions de rappel à exécuter:

// some sample async functions
var resolveAfter2Seconds = function() {
  console.log("starting slow promise -> ");
  return new Promise(resolve => {
    setTimeout(function() {
      resolve("slow");
      console.log("<- slow promise is done");
    }, 2000);
  });
};

var resolveAfter1Second = function() {
  console.log("starting fast promise ->");
  return new Promise(resolve => {
    setTimeout(function() {
      resolve("fast");
      console.log("<- fast promise is done");
    }, 1000);
  });
};

//a function that accepts a callback and would await its execution
async function foo(callback) {
  console.log("-- some code --");
  await callback();
  console.log("-- some more code --");
}

//calling with an async function that combines any code you want to execute
foo(async () => { 
  await resolveAfter2Seconds();
  await resolveAfter1Second();
})


3 commentaires

TY beaucoup pour le bon exemple! Maintenant, imaginez que j'ai des objets dans la définition de la fonction asynchrone foo (), qui ont été créés après --- du code --- et avant le rappel, et que j'ai des milliers de doubles-waits différents (maintenant stockés dans des fichiers), lorsqu'ils sont secondaires en appelant cette fonction foo (), qui veulent utiliser ces objets.


ths n'explique pas comment obtenir une valeur d'expression à partir de l'async, uniquement pour appeler une fonction plus tard


@wowow quel est le problème? const result = await callback() obtiendra la valeur. Je ne pensais pas qu'il fallait le déclarer explicitement, car il n'y a aucun changement dans la façon dont vous feriez cela. Je me concentrais sur la manière d'éviter l' eval .



4
votes

J'ai fini par utiliser la réponse d'Ermir:

let ctxScript = '(async () => {await foo1();await foo2();is_script_ended = true; })();';

async function foo()
{
  // a lot of code
  is_script_ended = false;
  eval( ctxScript );
  while(!is_script_ended){ await sleep(1000); }
  // a lot of code
}


1 commentaires

Dormir n'est jamais vraiment une bonne idée dans le code hé. Si votre script eval échoue, il se mettra en veille pour toujours jusqu'à ce que vous redémarriez le système. Si vous voulez vraiment utiliser le sommeil, enveloppez peut-être tout dans un essai, de sorte que si le code se brise, votre sommeil s'arrête. Quelque chose comme try {eval(ctxScript)} catch () {is_script_ended = true}



1
votes

Si vous voulez pouvoir attendre l'évaluation, vous pouvez utiliser ceci:

const testVar = "Hello world";
globalThis["testVar"] = testVar;
const result = await Object.getPrototypeOf(async function() {}).constructor(`
    console.log(testVar);
    await myAsyncFunc();
    return testVar;
`)();
// result will be "Hello world"
delete globalThis["testVar"];

Cela utilise le constructeur AsyncFunction . MDN contient une page qui décrit les différences entre son utilisation et l'utilisation de eval :

Remarque: les fonctions asynchrones créées avec le constructeur AsyncFunction ne créent pas de fermetures dans leurs contextes de création; ils sont toujours créés dans la portée globale.

Lors de leur exécution, ils ne pourront accéder qu'à leurs propres variables locales et globales, et non à celles de la portée dans laquelle le constructeur AsyncFunction a été appelé.

Ceci est différent de l'utilisation de eval avec du code pour une expression de fonction asynchrone.

Cela signifie que si vous avez des variables auxquelles vous souhaitez que votre code évalué puisse accéder, vous devez les ajouter à globalThis :

await Object.getPrototypeOf(async function() {}).constructor("your code here")();


1 commentaires

Exécutez-vous l'exemple dans une fonction asynchrone? L'exemple crée une fonction asynchrone et l'appelle à l'aide de wait, vous devez donc être dans une fonction async pour l'utiliser.