Reputation: 1260
I have one data
variable in my app, It is message
, And also I have one method in methods which performs some cryptographic algorithms.
Here is my code.
export default {
data: () => ({
message: ""
}),
methods: {
click() {
this.message = "Hello";
console.log("this.message ", this.message); // Prints "Hello"
// takes around 8 seconds
var encryptedPassphrase = generateKeystore();
console.log("this.message ", this.message); // Prints "Hello"
}
}
};
Above message
variable I'm displaying in HTML tag, And method click
gets called from Vuetify button.
Below is HTML code,
<div>
<p>{{message}}</p>
<v-btn @click="click">Click</v-btn>
</div>
Issue
Now Problem is When click
method is called first task it does is updates message
variable, But this message
variable update reflects in HTML after the full function execution is completed. So In click
function next task is cryptographic computation which takes around 8 seconds, After this completes message
reflects in HTML. I have no idea what is going on here.
Just mentioning I'm using webpack
here.
<v-btn @click="update(); click();">Click</v-btn>
Even this doesn't work, Here update
method updates message
variable, It updates after click
function is completed.
Upvotes: 2
Views: 203
Reputation: 21
If you want the message data field that's changed in the click method to reflect in the html before the method is complete, you have to wrap the cryptographic function in a Vue.nextTick
. That will ensure that the logic inside the nextTick
will execute on the next DOM
update cycle. This will allow the DOM
to be updated with the latest value for message and then it will execute the cryptographic function. Otherwise, Vue
will wait for the click function to complete prior to the changes being reflected in the DOM
.
Vue.nextTick(function() { //cryptographic function })
https://v2.vuejs.org/v2/api/#Vue-nextTick
Upvotes: 1
Reputation: 1248
Long story short, in the case, you should use Web Worker for intensive tasks such as your generateKeystore
function. The reason your message
didn't get rendered is that only one piece of Javascript ever executing at a time, basically your generateKeystore
blocks the Vue rendering (watcher etc) until it finishes.
I've created a js fiddle to demonstrate the Vue - Web Worker usage https://jsfiddle.net/Fourzero/t9L1g2hj/17/. You can also find the code below. In this example, the click
handler has the same problem your described (changed message won't render until slow function is done), the click2
method handles it in a way that the message
'hello' get rendered immediately, and the slow function starts executing (takes a few seconds) and will return value once finished.
<script id="worker1" type="javascript/worker">
// This script won't be parsed by JS engines because its type is javascript/worker.
self.onmessage = function(e) {
console.log('slow function started execution inside worker with parameter: ', e.data)
// execute slow function
var sum = 1;
for (let i = 1; i < 10000000000; i++) {
sum = sum + i;
}
console.log('DONE!!slow function finished inside worker with result: ' + sum)
self.postMessage(sum);
};
</script>
<div id="app">
<p ref="message">{{message}}</p>
<p >slowFunctionResult: {{slowFunctionResult}}</p>
<button @click="click">Click</button>
<button @click="click2">Click (Run in worker)</button>
</div>
var blob = new Blob([
document.querySelector('#worker1').textContent
], { type: "text/javascript" })
// Note: window.webkitURL.createObjectURL() in Chrome 10+.
var worker = new Worker(window.URL.createObjectURL(blob));
new Vue({
el: '#app',
data: {
message: 'xx',
slowFunctionResult: ''
},
methods: {
click() {
this.message = 'hello';
console.log("slow function start")
this.slowFunction();
console.log("slow function done")
// the next tick won't happen until the slow function finished
},
click2() {
this.message = 'hello';
let that = this;
worker.onmessage = function(e) {
console.log("Received result from worker!: " + e.data);
that.slowFunctionResult = e.data;
}
// DOM should be updated before the slow function execution finished
worker.postMessage(this.message); // Start the worker.
},
slowFunction() {
var sum = 1;
for (let i = 1; i < 10000000000; i++) {
sum = sum + i;
}
}
}
})
Console output of clicking the Click (Run in worker)
button
slow function started execution inside worker with parameter: hello
// after a few seconds
DONE!!slow function finished inside worker with result: 49999999990067860000
Received result from worker!: 49999999990067860000
Upvotes: 0