1
votes

Composant de mise à jour Vue pendant l'appel de fonction synchrone

J'ai un bouton déclenchant une fonction à exécution longue (gros calcul synchrone fait localement):

<button :disabled="running" @click="run">Run</button>

data () {
 return {
   running: false;
 }
}

methods: {
  run() {
    this.running=true;
    veryLongRunningCall();
    this.running=false;
  }
}

Mais vue ne met à jour le bouton qu'après la fin de la fonction run (). Comment dire à vue de forcer la mise à jour du bouton après avoir défini l'exécution sur true? J'ai essayé de rendre la fonction d'exécution asynchrone et d'appeler ceci. $ NextTick () mais cela ne fonctionne pas.


2 commentaires

veuillez mentionner ce qu'il y a à l'intérieur de cette fonction veryLongRunningCall


Rien d'intéressant à l'intérieur de veryLongRunningCall, pourrait être sleep (5000).


3 Réponses :


1
votes

Javascript est un langage à thread unique et il chante une boucle d'événement pour gérer sa synchronisation. C'est pourquoi il n'y a pas de sleep dans JS et à la place nous avons setTimeout qui traite des événements de temps.

Donc, pour résoudre votre problème, vous devez trouver un moyen de rendre votre longRunningCall aussi asynchrone (et basé sur des événements) que possible, car tout appel synchrone arrêtera complètement tous les javascript de la page.

Vous pouvez avoir une lecture ici: https://exploringjs.com/impatient-js/ch_async-js.html

Réponse précédente

Vue s'exécute par cycles appelés ticks et ne mettra à jour les vues qu'une fois le tick terminé. Vous pouvez cependant le forcer à exécuter la tâche après le tick courant:

methods: {
  run() {
    this.running=true;
    Promise.resolve()
      .then(veryLongRunningCall)
      .then(() => this.running = false)
  }
}

Vue.nextTick (disponible sur prototype sous le nom this. $ nextTick ) enregistrera un rappel à exécuter après la fin de la graduation actuelle, donc il exécutera veryLongRuningCall après la mise à jour de la vue pour refléter this.running = true

Cependant, cela entraînera la prochaine tick à prendre beaucoup plus de temps que nécessaire - ce qui pourrait empêcher les autres éléments de mettre à jour leur vue jusqu'à ce que cette tâche particulière soit terminée. C'est une bien meilleure solution pour rendre votre veryLongRunningCall asyncrone (à moins que vous n'ayez une très bonne raison pour ne pas le faire). Vous pouvez facilement utiliser Promise pour cela:

methods: {
  run() {
    this.running=true;
    this.$nextTick(() => {
      veryLongRunningCall();
      this.running=false;
    }
  }
}


2 commentaires

Aucune des solutions ne fonctionne. J'ai déjà essayé de gérer avec $ nextTick mais il semble que je manque quelque chose


Intéressant, je pense que ce n'est pas vraiment un problème de Vue mais plutôt le résultat de l'architecture javascript. J'ai mis à jour la réponse.



1
votes

Je suppose que le problème est que vous bloquez le fil principal. Vous pouvez envelopper votre très longue tâche dans une promesse, elle sera transmise à l'API Web, puis à la que de rappel, et la boucle d'événement saisit votre rappel de la que et la place sur la pile d'appels, et une fois qu'elle atteint la pile d'appels, le le thread principal est de nouveau bloqué jusqu'à ce que cette tâche soit terminée. Donc, je dirais que vous allez peut-être avec un fil de travail.

Vous devez également savoir que vous avez un accès limité au Webworker, comme vous ne pouvez pas accéder au dom


0 commentaires

1
votes

J'ai trouvé la solution:

methods: {
  run() {
    this.running=true;
    setTimeout(()=>{
      veryLongRunningCall();
      this.running=false;
    },0);
  }
}

Je redonne donc le contrôle à la boucle d'événement principale pour qu'elle puisse rafraîchir la page et après exécuter ma tâche contenant l'appel veryLongRunningCall (). p >

Cela fonctionne mais ce serait mieux (et parfait!) s'il y avait un autre moyen dédié que setTimeout pour ajouter une tâche à la boucle de l'événement principal.

Merci BroiSatse, vous m'avez mis sur le chemin!


1 commentaires

Oui, cela a fait l'affaire pour moi aussi! Je ne peux pas ébranler le sentiment que cette approche est un peu «hacky», mais pour le moment, cela fera l'affaire.