обещанията на angular са подвеждащи, тъй като не винаги се извикват асинхронно

Наскоро отстранявах грешки в някои неща, свързани с обещания в angular.js, и забелязах, че angular поставя резолюции на обещания в своя цикъл evalAsync, както е показано в тази диаграма. Останах с впечатлението, че обратните извиквания на обещание винаги се изпълняват асинхронно (като ново събитие в опашка за събития). Въпреки това, с механизма на angular е възможно, ако някое от обещанията бъде разрешено по време на цикъл на дайджест и angular започне друга итерация при дайджест, обратното извикване за обещанието ще бъде извикано в същия стек за изпълнение, тъй като evalAsync опашката винаги се проверява първо:

do { // "while dirty" loop
  dirty = false;
  current = target;

  while (asyncQueue.length) {
    try {
      asyncTask = asyncQueue.shift();
      asyncTask.scope.$eval(asyncTask.expression, asyncTask.locals);
    } catch (e) {
      $exceptionHandler(e);
    }
    lastDirtyWatch = null;
  }

  traverseScopesLoop:
  do { // "traverse the scopes" loop
  ...
  } while ((current = next));
...  
} while (dirty || asyncQueue.length);

Не е ли подвеждащо?


person Max Koretskyi    schedule 18.04.2015    source източник
comment
Това е начинът, по който обещанията трябва да работят. Една функция не може наистина да разбере дали е в един конкретен цикъл на събитието или в друг.   -  person Pointy    schedule 18.04.2015
comment
Благодаря, не го знаех. Останах с впечатлението, че обещанията винаги се решават в нов цикъл на събития, тъй като разрешаването в същия цикъл на събития не е наистина асинхронно, нали?   -  person Max Koretskyi    schedule 18.04.2015
comment
Това е вярно, но тъй като вашето обратно извикване не знае какво ще се случи, то трябва да бъде написано така, сякаш е извикано асинхронно.   -  person Pointy    schedule 18.04.2015
comment
Всъщност след известно четене смятам, че коментарите ми са грешни; спецификацията на обещанието е по-фина и стабилна, отколкото си мислех, и не знам достатъчно какво се случва в Angular, за да го преценя.   -  person Pointy    schedule 18.04.2015


Отговори (1)


Не съм сигурен дали бих нарекъл това подвеждащо или не. Факт е:

  • Обратните извиквания на Promise винаги се изпълняват асинхронно. Те никога не се извикват, преди да се върне .then(), към който са били предадени.
    В по-голяма степен те дори никога не се извикват от потребителски код - има "само код на платформа" в стека, както е посочено от спец. Promises/A+.
  • Не е гарантирано, че всяко асинхронно обратно извикване се изпълнява при собствен цикъл на събитие. Няма изискване две обратни извиквания да не споделят един и същ цикъл на събитие. В крайна сметка те така или иначе не могат да различат това.

Във вашия случай Angular се квалифицира като код на платформа, който използва свой собствен "цикъл на събитията" - цикълът на дайджест.

person Bergi    schedule 18.04.2015
comment
Така че, ако предам функция на .then() метод на вече изпълнен екземпляр на обещание, внедряването трябва да направи нещо като планиране на незабавно изчакване, за да го извика? Или да изчакате, докато някой интервален таймер го намери? Това е странно, но спецификацията със сигурност се чете по този начин. - person Pointy; 18.04.2015
comment
@Pointy: Точно така. Спецификацията гарантира асинхронност. Без странни ъглови случаи, при които вашето обратно извикване понякога се случва преди .then(…) да се върне, само защото обещанието неочаквано вече е изпълнено. - person Bergi; 18.04.2015
comment
Добре де. Дискусията за грешката setImmediate() gecko е объркана, но информативна. Този въпрос изглежда интересен и измамно сложен, отвъд основните поведенчески гаранции (които не са сложни). - person Pointy; 18.04.2015
comment
@Bergi, благодаря, така че, доколкото разбирам, изводът тук е, че обратните извиквания никога не се извикват преди .then(), към който са били предадени, връща, но обратното извикване може да бъде изпълнява се в същия цикъл на събития като функция, извикваща .then() метод от някакъв код на платформата, когато стекът с потребителски код съществува, нали? - person Max Koretskyi; 18.04.2015
comment
@Maximus: Да, може да се изпълни в същото завъртане на цикъл на събитие като извикването към then, но не и когато все още има някакъв потребителски код в стека. (Измислен) пример би бил p.then(function(){ p.then(/*… later */); …} /* can be *now* */) - вътрешното обратно извикване може да бъде извикано веднага след връщането на външното. - person Bergi; 18.04.2015
comment
@Bergi, разбирам, благодаря. Но не разбрах примера ти. Можете ли да го публикувате като част от отговора си в четим формат. Форматирах го и изглежда, че сте поставили /* can be *now* */ част на грешното място или пропускам нещо. - person Max Koretskyi; 18.04.2015
comment
@Maximus: Не, нарочно го сложих след първата функция. Но вероятно е било лоша идея да поставим това в код. Това, което имах предвид, беше, че в ситуация promise.then(function a(){ … promise.then(b); … }) и двете обратни извиквания могат да бъдат изпълнени в един и същи ход на цикъла на събитието: a(); b(); дори ако b е регистрирано в обещанието в a(). Но от обхвата на функцията a няма никаква разлика, b винаги се изпълнява по-късно, след потребителския код . - person Bergi; 18.04.2015
comment
@Bergi, казваш, че и двете обратни извиквания могат да бъдат изпълнени в един и същи ход на цикъла на събитието. Искаш да кажеш, че при определени обстоятелства няма да го направят? - person Roamer-1888; 18.04.2015
comment
@Roamer-1888: Имах предвид, че това зависи от изпълнението. Реализацията може да избере да отложи такива повиквания до следващия ред или да ги изпълни възможно най-скоро. Angular изглежда го прави по-късно. - person Bergi; 18.04.2015
comment
@Bergi, благодаря ти за обяснението. сега има пълен смисъл. - person Roamer-1888; 18.04.2015