Яка різниця між повертається значенням або Promise.resolve від тоді ()


314

Яка різниця між:

new Promise(function(res, rej) {
    res("aaa");
  })
  .then(function(result) {
    return "bbb";
  })
  .then(function(result) {
    console.log(result);
  });

і це:

new Promise(function(res, rej) {
    res("aaa");
  })
  .then(function(result) {
    return Promise.resolve("bbb");
  })
  .then(function(result) {
    console.log(result);
  });

Я запитую, як я отримую різну поведінку, використовуючи послугу Angular та $ http з ланцюжком .then (). Трохи забагато коду, тому спочатку наведений вище приклад.


1
Яку "різну поведінку" ти бачиш? Обидва приклади повинні працювати і поводитися приблизно однаково. У Promise.resolve()другому прикладі непотрібно.
JLRishe

4
@pixelbits Нічого поганого thenв тому, що повернути обіцянку від обробника, насправді, це ключовий аспект специфікації обіцянок, що ви можете це зробити.

Зауважте, що це працює з довільно вкладеними thens - термін "інші мови" для цього thenє і a, mapі a flatMap.
Бенджамін Грюнбаум

1
у другому рядку, чому ви повинні викликати res ("aaa"), чому повернення "aaa" не може бути достатнім, а "Обіцяти улов" для вирішення () це так само, як і вилучення винятків для відхилення ()?
Сем Ліддікотт

1
@SamLiddicott має те саме питання, а міни трохи складніші: new Promise((res, rej) => { return fetch('//google.com').then(() => { return "haha"; }) }).then((result) => alert(result));цей код просто зависне (не вирішений назавжди). Але якщо я перейду return "haha";до return res("haha");цього, він спрацює і попередить "ха-ха". Чи не витяг (). Тоді () вже завершив "ха-ха" в рішучу обіцянку?
Шаун Чен

Відповіді:


138

Правило полягає в тому, що якщо функція, що знаходиться в thenобробнику, повертає значення, обіцянка вирішується / відхиляється з цим значенням, і якщо функція повертає обіцянку, що трапляється, наступним thenпунктом буде thenпункт обіцянки, функція повертається , тож у цьому випадку перший приклад потрапляє через звичайну послідовність thensта виводить значення, як можна було очікувати, у другому прикладі об'єктом обіцянки, який повертається, коли ви це робите Promise.resolve("bbb"), є тим, thenщо викликається при ланцюгу (для всіх намірів і цілей). Те, як це насправді працює, описано нижче більш докладно.

Цитуючи з специфікацій Promises / A +:

Процедура розв’язування обіцянки - це абстрактна операція, яка приймає як вхід обіцянку і значення, яке ми позначаємо як [[Resolve]](promise, x). Якщо xце є підданим, воно намагається змусити обіцянку прийняти станx , за умови, що х поводиться хоча б дещо як обіцянка . В іншому випадку він виконує обіцянку зі значенням x.

Таке поводження з thenables дозволяє реалізовувати реалізацію обіцянок до тих пір, поки вони виявляють метод Promis / A +-compliant, а потім. Це також дозволяє реалізаціям Promises / A + "асимілювати" невідповідні реалізації з розумними методами.

Головне, що слід помітити тут, це цей рядок:

якщо xце обіцянка, прийміть свою державу [3.4]

посилання: https://promisesaplus.com/#point-49


4
"Прийняти свій стан" - це стислий і корисний спосіб висловити поведінку, коли thenобробник поверне обіцянку. +1 для посилання на специфікацію.

69
Насправді - відповідна частина специфікації тут полягає в тому, що [[Resolve]]викликається як у можливостях, так thenі в значеннях, тому по суті воно обертає значення з обіцянкою, так return "aaa"само як return Promise.resolve("aaa")і return Promise.resolve("aaa")є те саме, що return Promise.resolve(Promise.resolve("aaa"))- оскільки розв’язання є ідентичним, викликаючи його на значення більше ніж один раз має однаковий результат.
Бенджамін Груенбаум

8
@Benjamin Gruenbaum це означає, що повернення "aaa"і return Promise.resolve("aaa")взаємозамінні можливості thenв будь-яких випадках?
CSnerd

9
Так, саме це означає.
Бенджамін Грюнбаум

118

Простіше кажучи, всередині thenфункції обробника:

А) Коли xзначення (число, рядок тощо):

  1. return x еквівалентно return Promise.resolve(x)
  2. throw x еквівалентно return Promise.reject(x)

Б) Коли xвже обіцяна Обіцянка (вже не очікується):

  1. return xеквівалентно return Promise.resolve(x), якщо Обіцянка вже була вирішена.
  2. return xеквівалентно return Promise.reject(x), якщо Обіцяння вже було відхилено.

C) Коли xчекає Обіцянка:

  1. return xповерне очікуване Обіцяння, і воно буде оцінено на наступному then.

Детальніше про цю тему читайте в документах Promise.prototype.then () .


93

Обидва ваші приклади повинні поводитися майже однаково.

Значення, повернене всередині then()обробника, стає роздільною здатністю обіцянки, повернутої з нього then(). Якщо значення, повернене всередині, .then є обіцянкою, обіцянка, повернута then()волею, "прийме стан" цієї обіцянки і вирішить / відхилить так само, як і повернута обіцянка.

У вашому першому прикладі ви повертаєтеся "bbb"в перший then()обробник, тому "bbb"передається в наступний then()обробник.

У вашому другому прикладі ви повертаєте обіцянку, яка негайно розв’язується зі значенням "bbb", тому "bbb"передається наступному then()обробнику. ( Promise.resolve()Тут стороннє).

Результат такий же.

Якщо ви можете показати нам приклад, який насправді проявляє різну поведінку, ми можемо сказати вам, чому це відбувається.


1
Гарна відповідь! Що щодо Promise.resolve();vs return;?
FabianTe

2
@FabianTe Вони також матимуть такий же ефект, за винятком, а undefinedне з "bbb".
JLRishe

51

Ви вже отримали хорошу формальну відповідь. Я подумав, що слід додати короткий.

Наступні речі ідентичні обіцянкам / обіцянкам:

  • Виклик Promise.resolve(у вашому кутовому випадку це $q.when)
  • Виклик конструктора обіцянок і вирішення в його резолюції. У вашому випадку це так new $q.
  • Повернення значення з thenзворотного дзвінка.
  • Виклик Promise.all на масив зі значенням, а потім витягніть це значення.

Отже, усі є однаковими для обіцянки або простого значення X:

Promise.resolve(x);
new Promise(function(resolve, reject){ resolve(x); });
Promise.resolve().then(function(){ return x; });
Promise.all([x]).then(function(arr){ return arr[0]; });

І це не дивно, що специфікація обіцянок базується на процедурі вирішення обіцянки, яка дозволяє легко взаємодіяти між бібліотеками (наприклад, $ q та власними обіцянками) і полегшує ваше життя в цілому. Кожного разу, коли може виникнути рішення щодо обіцянки, виникає резолюція, що створює загальну послідовність.


я можу запитати, який сенс робити Promise.resolve().then(function(){ return x; });? Я виявив, що фрагмент робить щось подібне (це називається функцією всередині thenблоку). Я думав, що це більш-менш, як робити тайм-аут, але це трохи швидше. jsben.ch/HIfDo
Sampgun

Немає сенсу, що це так само, як Promise.resolve (x) у 99,99% випадків. (0,001% - це те, що ми перебуваємо в withблоці над об'єктом або проксі-сервером з xдоступом до властивостей, який видаляє виняток. У такому випадку Promise.resolve (x) призведе до помилки, яку викинули, але Promise.resolve().then(function(){ return x; });буде відхилено обіцянку, оскільки помилка викинута в а then).
Бенджамін Грюнбаум

ви пов’язали порожній блиск, або ви не зберегли. У всякому разі, я не говорив про відмінності між твердженнями. Я говорив саме про те, що написав. Просто щоб бути більш ясним, що це фрагмент коду я говорив: if (validator) { Promise.resolve().then(() => { this._cdRef.markForCheck(); }); }. Тут обіцянка не призначена, тож у чому справа? Час очікування матиме (більш-менш) такий же ефект, чи ні?
Сампгун

1
Він виконує виклик асинхронно після того, як відбувся весь синхронний код, але до того, як трапляється будь-який ввід / вивід. Це називається "семантика мікротиків".
Бенджамін Груенбаум

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.