web-dev-qa-db-fra.com

L'async attend-il vraiment de ne pas bloquer dans le navigateur?

J'ai joué avec la fonctionnalité dans un SPA en utilisant TypeScript et des promesses natives, et je remarque que même si je refaçonne une fonction de longue durée en une fonction asynchrone renvoyant une promesse, l'interface utilisateur ne répond toujours pas.

Mes questions sont donc:

  • Comment la nouvelle fonctionnalité asynchrone/attente permet-elle d'éviter de bloquer l'interface utilisateur dans le navigateur? Existe-t-il des étapes supplémentaires spéciales à utiliser lors de l'utilisation de async/wait pour obtenir une interface utilisateur réactive?

  • Quelqu'un peut-il créer un violon pour montrer comment l'asynchronisation/l'attente aide à rendre l'interface utilisateur réactive?

  • Comment async/wait se rapporte-t-il aux fonctionnalités asynchrones antérieures telles que setTimeout et XmlHttpRequest?

30
prmph

await p planifie l'exécution du reste de votre fonction lorsque la promesse p se résout. C'est tout.

async vous permet d'utiliser await. C'est (presque) tout ce qu'il fait (il enveloppe également votre résultat dans une promesse).

Ensemble, ils font lire le code non bloquant comme un code de blocage plus simple. Ils ne débloquent pas le code.

Pour une interface utilisateur réactive, déchargez le travail gourmand en ressources processeur sur un thread travailleur et passez-lui des messages:

async function brutePrime(n) {
  function work({data}) {
    while (true) {
      let d = 2;
      for (; d < data; d++) {
        if (data % d == 0) break;
      }
      if (d == data) return self.postMessage(data);
      data++;
    }
  }

  let b = new Blob(["onmessage =" + work.toString()], {type: "text/javascript"});
  let worker = new Worker(URL.createObjectURL(b));
  worker.postMessage(n); 
  return await new Promise(resolve => worker.onmessage = e => resolve(e.data));
}

(async () => {
  let n = 700000000;
  for (let i = 0; i < 10; i++) {
    console.log(n = await brutePrime(n + 1));
  }
})().catch(e => console.log(e));
54
jib

async est une manière plus élégante de structurer du code asynchrone. Il ne permet aucune nouvelle capacité; c'est juste une meilleure syntaxe que les rappels ou les promesses.

Ainsi, async ne peut pas être utilisé pour "rendre quelque chose asynchrone". Si vous avez du code qui doit faire beaucoup de traitement basé sur le processeur, async ne rendra pas l'interface utilisateur comme par magie. Ce que vous devez faire est d'utiliser quelque chose comme travailleurs Web , qui sont l'outil approprié pour pousser le travail lié au CPU à un thread d'arrière-plan afin de rendre l'interface utilisateur réactive.

14
Stephen Cleary

JavaScript est monothread et s'exécute dans le même thread que l'interface utilisateur. Ainsi, tout le code JavaScript bloquera l'interface utilisateur. Comme mentionné par d'autres, les travailleurs Web peuvent être utilisés pour exécuter du code dans d'autres threads, mais ils ont des limites.

La différence entre les fonctions asynchrones et les fonctions régulières est qu'elles renvoient une promesse. À l'aide d'un rappel, vous pouvez ensuite différer l'exécution de code, qui gère le résultat d'un appel de fonction et permettant ainsi à l'interface utilisateur d'effectuer un certain travail. Les trois exemples suivants ont le même effet:

async function foo() {
  console.log("hi");
  return 1; 
}
foo().then(result => console.log(result))
console.log("lo");

function foo() {
  console.log("hi");
  return 1; 
}
Promise.resolve(foo()).then(result => console.log(result))
console.log("lo");

function foo() {
  console.log("hi");
  return 1; 
}
const result = foo();
setTimeout(() => console.log(result));
console.log("lo");

Dans les trois cas, la console enregistre hi, lo, 1. Avant que 1 ne soit imprimé, l'interface utilisateur peut gérer les entrées utilisateur ou dessiner des mises à jour. La raison 1 imprimée en dernier dans les deux premiers cas est que les rappels de promesses ne sont pas exécutés immédiatement.

await vous permet de le faire sans rappels:

async function foo() {
  console.log("hi");
  return 1; 
}

async function bar() {
  const result = await foo();
  console.log(result);
}

bar();
console.log("lo"); 

Cela affichera également hi, lo, 1. Tout comme un rappel pour une promesse, le code après await n'est jamais exécuté immédiatement.

10
a better oliver