SetState is now async

Hi, Vlad.
Congrats for the amazing v5 :wink:
When executing two (near) consecutive:
$setState(…).then(…)

$setState(…).then(…)

Since the second setState is “painted” before the updated first (which it’s ok), the first “then” is never called.

The question is: should the first THEN be executed -finally- whenever the update occurs (in this case, in the second setState)? Right now, it never executes. Is that the correct implementation expected?
Thanks a lot!

It is better to avoid setState in this case and do something like:

this.foo = 'bar';
this.john = 'doe';

this.$update().then(...)

@nolimits4web можете прояснить как быть на реальном проекте с асинхронным setState?

В Router Component есть много подписок на события через EventBus:

events.on('update-counter', function () {
   self.$setState(...).then();
});
...
events.on('update-user-info', function () {
   self.$setState(...).then();
});
...
events.on('update-products-list', function () {
   self.$setState(...).then();
});

В каждом случае код, который в then, обязателен к выполнению. Но согласно вашему ответу, если события придут почти одновременно, то какой-то then() может быть пропущен.

Также есть еще методы компонента, например обработка клика, в них тоже есть setState и then, которые нельзя пропускать.

Thanks for the response @nolimits4web . After reading the code under ‘component-class.js’, I fully understand the behavior.

@shastox, maybe we could use ‘$tick().then()’. It’s not perfect, but (I think) it always executes the ‘then()’. I will try to check it.

То, что вы посмотрели код, не означает, что вы поняли как JS работает под капотом, иначе бы не было вопросов насчет tick().

Мне не понятна реализация асинхронного setState, идея крутая, но почему setState / update вызывает cancelAnimationFrame? Ведь это должно остановить другие вызовы? По идее, тут должна быть очередь обновлений, т.е. каждый setState добавляется в конец очереди без возможности отмены. Также должна быть проверка на то, “идет” ли сейчас очередь, чтобы можно было на стороне безопасно работать с DOM.

В tick считается, что второй вызов requestAnimationFrame уже точно будет тогда, когда update будет завершен, но почему между строчками:

 if (self.__updateIsPending) {

  window.requestAnimationFrame(() => {  

не может вклиниться новый setState? Тогда все сломается?

На данный момент безопасно с асинхронным setState можно работать только на простых штуках, как и написал Владимир, но это совсем не то, что действительно используется в средних и сложных проектах.

It will be fixed in upcoming update. But do you understand guys, that you need to use that updated callback ONLY if you need to do some manual manipulation with DOM after component re-rendered, right?

Absolutely understood, @nolimits4web. In fact, it is used only for that. I just wanted to know if the setState callback behavior was buggy or created so expressly.
In my specific case, the problem was that sometimes (when using async database calls) you cannot guarantee the execution of the callbacks. And in that case, no callback were called.

Once again THANKS a lot for your support and congrats for such a wonderful F7 v5 :wink:

Да, нужно быть уверенным что DOM точно обновлен

@nolimits4web получается, что мы не можем использовать setState внутри себя, к примеру:

this.$update(function () {
   self.$setState({
      var : 'val'
   });
);

Причина, как я понимаю, здесь: https://github.com/framework7io/framework7/blob/master/src/core/modules/component/component-class.js#L278 т.е. система на “рекурсию” не рассчитана.

Это норма или все же ошибка?

Такой код тоже не сработает:

this.$update();
this.$tick(function () {
   self.$setState({
      ...
   });
});

Ведь $tick тоже идет через очередь.

Вот такой код сработает отлично:

this.$update();
this.$setState('...');

С одной стороны - результат будет таким как нужно, но с другой это не то же самое, ведь мы не можем “вставить” никакой код посередине.

1 Like

Ничего не понял :), что не получается сделать, есть какой-то приближенный к реальности пример?