#javascript #asynchronous #vue.js
#javascript #асинхронный #vue.js
Вопрос:
Я пытаюсь добавить экран загрузки, который отображается, пока мое приложение выполняет дорогостоящее хэширование и расшифровку некоторых данных (занимает 2-3 секунды). Когда я экспериментально удаляю дорогостоящие части, следующие за тем, что должно обновлять DOM, экран загружается немедленно. В противном случае он не будет отображаться до завершения дорогостоящей функции. Есть идеи, что я напортачил?
export default {
data(){
return {
loading: false,
password: ''
}
},
methods: {
async unlock(){
this.loading = true;
await this.$nextTick()
// DOM should update, but it doesn't
try{
await this.$root.UnlockVaultPassword(this.password);
} catch(err){
this.loading = false;
return;
}
// DOM updates once finished with hashing and ciphering
this.loading = false;
this.$router.push({name: 'vault'});
}
}
}
Я думаю, что, поскольку vue обновляет dom асинхронно, я испытываю состояние гонки, когда он не может поместиться в обновление dom во время хэширования. Как я могу ДОЖДАТЬСЯ завершения dom?
Комментарии:
1. Одна только настройка
this.loading = true
запускает повторный запуск?2. правильно. Это определенно как-то связано с вычислительной дороговизной.
3. Как насчет того, чтобы попробовать это.$forceUpdate(), чтобы заставить vue обновить DOM.
4. Предостережение с
$forceUpdate
: он не будет повторно отображать все дочерние элементы.5. Да, я пробовал это, не работает. Рендеринг запускается, но по какой-то причине хэширование мешает
Ответ №1:
Может быть, попробовать использовать mounted
перехват? И вызывайте unlock
функцию как отдельную операцию после $nextTick
для хорошего разделения проблем.
mounted () {
this.loading = true;
this.$nextTick(function () {
this.unlock();
})
}
methods: {
unlock: function() {
// unlock code
this.loading = false;
}
}
ОБНОВЛЕНИЕ: попробуйте использовать функцию-обработчик, которая сначала устанавливается loading
при отправке формы, затем откладывается на unlock
в качестве обратного вызова на $nextTick
, вот так:
methods: {
handleFormSubmit: function() {
this.loading = true;
this.$nextTick(this.unlock);
},
unlock: function() {
// unlock code
this.loading = false;
}
}
ОБНОВЛЕНИЕ 2: если ничего из вышеперечисленного не работает, это начинает звучать как ошибка в ожидаемом поведении $nextTick
, поскольку рендеринг не завершен до выполнения следующего кода. Что произойдет, если вы используете setTimeout
для принудительной разблокировки процессов до конца стека выполнения?
methods: {
handleFormSubmit: function() {
this.loading = true;
this.$nextTick(() => {
setTimeout(this.unlock, 0);
});
},
unlock: function() {
// unlock code
this.loading = false;
}
}
Комментарии:
1. Проблема с этим заключается в том, что функция «разблокировать» запускается при отправке формы
2. Ах. Но как насчет передачи
unlock
обратного вызова в$nextTick
? Позвольте мне изменить мой пример.3. Я думаю, что, возможно, есть условие гонки, при котором
$nextTick
выполняется, но DOM не был полностью повторно отрисован ко времениUnlockVaultPassword
вызова. Кажется странным, потому что ваш код выглядит правильно.4. Другое предложение: выгрузка сложного хэширования / расшифровки в асинхронный процесс, либо на серверную часть, либо, возможно, на веб-работников . В любом случае может иметь преимущества в производительности, если ваш код расшифровки блокирует однопоточные процессы JS на 2-3 секунды.
5. К сожалению, тот же эффект, и я не могу выгрузить на серверную часть, потому что приложение не полагается на серверную часть (electron)
Ответ №2:
Итак, я нашел обходной путь… Мне это не нравится, но это не так уж ужасно. На самом деле я могу включить обратный вызов в цикл событий, так что мое хэширование произойдет только после завершения рендеринга.
export default {
data(){
return {
loading: false,
password: ''
}
},
methods: {
async unlock(){
this.loading = true;
setTimeout(() => {
try{
await this.$root.UnlockVaultPassword(this.password);
} catch(err){
this.loading = false;
return;
}
this.loading = false;
this.$router.push({name: 'vault'});
}, 0)
}
}
}