В чем разница между возвращаемым значением или Promise.resolve от then ()

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 (). Слишком много кода, поэтому сначала пример выше.

spirytus
источник
1
Какое «другое поведение» вы видите? Оба примера должны работать и вести себя примерно одинаково. Во Promise.resolve()втором примере нет необходимости.
JLRishe
4
@pixelbits В возврате обещания от thenобработчика нет ничего плохого , на самом деле, это ключевой аспект спецификации обещаний, что вы можете это сделать.
Обратите внимание, что это работает с произвольно вложенными thens - термин «другие языки» для этого - это thenи a, mapи a flatMap.
Бенджамин Грюнбаум
1
в строке 2 почему вы должны вызывать res ("aaa"), почему не может быть достаточным возвращение "aaa", и Promise перехватывает для resol () его так же, как он перехватывает исключения для reject ()?
Сэм Лиддикотт
1
У @SamLiddicott тот же вопрос, в то время как мины немного сложнее: new Promise((res, rej) => { return fetch('//google.com').then(() => { return "haha"; }) }).then((result) => alert(result));этот код будет зависать (не разрешаться вечно). Но если я перейду return "haha";на return res("haha");то, это сработает и предупредит "ха-ха". Разве fetch (). Then () уже обернуло "хаха" в решенное обещание?
Шаунг Ченг

Ответы:

138

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

Цитирование из Обещаний / A + spec:

Процедура разрешения обещания - это абстрактная операция, принимающая в качестве ввода обещание и значение, которое мы обозначаем как [[Resolve]](promise, x). Если xэто возможно, он пытается заставить обещание принять состояниеx , исходя из предположения, что x ведет себя, по крайней мере, несколько как обещание . В противном случае он выполняет обещание со значением x.

Такое обращение с последующими объектами позволяет реализациям обещаний взаимодействовать, если они предоставляют способ, соответствующий обещаниям / A +, а затем метод. Это также позволяет реализациям Promises / A + «ассимилировать» несовместимые реализации разумными методами then.

Ключевой момент, на который следует обратить внимание, это строка:

если xобещание, прими его состояние [3.4]

ссылка: https://promisesaplus.com/#point-49

Hrishi
источник
4
«Принять его состояние» - это краткий и полезный способ выражения поведения, когда thenобработчик возвращает обещание. +1 за спецификацию.
69
На самом деле - релевантная часть спецификации здесь - это тот факт, что он [[Resolve]]вызывается как для thenспособностей, так и для значений, так что, по сути, он заключает в себе значение с обещанием, так return "aaa"же, как return Promise.resolve("aaa")и return Promise.resolve("aaa")то же самое, поскольку return Promise.resolve(Promise.resolve("aaa"))- решимость идемпотентна, вызывая его для значения, более чем один раз имеет тот же результат.
Бенджамин Грюнбаум
8
@ Бенджамин Грюнбаум, значит ли это, что возвращаются "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()Здесь посторонний).

Результат тот же.

Если вы можете показать нам пример, который на самом деле демонстрирует другое поведение, мы можем рассказать вам, почему это происходит.

JLRishe
источник
1
Хороший ответ! А как насчет Promise.resolve();против return;?
FabianTe
2
@FabianTe Те же будут иметь тот же эффект, за исключением с undefinedвместо "bbb".
JLRishe
51

Вы уже получили хороший официальный ответ. Я решил добавить короткий.

Следующие вещи идентичны обещаниям / обещаниям A + :

  • Вызов 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; });? Я обнаружил, что snipped делает что-то похожее (это называется функцией внутри thenблока). Я думал, что это более или менее похоже на тайм-аут, но это немного быстрее. jsben.ch/HIfDo
Сампгун
Нет смысла, что он такой же, как 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
Он выполняет вызов асинхронно после того, как произошел весь синхронный код, но до того, как произойдет какой-либо ввод-вывод. Это называется "семантика микротик".
Бенджамин Грюнбаум