Я не понимаю, что вы имеете в виду под [«Обещания нетерпеливы»].

Я имею в виду тот факт, что Promise тщательно оценивается, а не лениво. Давайте посмотрим на пример:

const p = new Promise((resolve) => {
  console.log('Hello world!');
  resolve(true);
});

Мы создаем Promise p, но еще не привязываем никаких обработчиков (мы не вызывали p.then()). Несмотря на это, Promise с радостью выполнит нашу функцию (да, в следующем тике я по-прежнему буду называть это немедленно). Вот что я имею в виду под нетерпением. Если мы выполним этот фрагмент кода, мы увидим «Hello world!» несмотря на то, что никогда не связывался с обработчиками.

Напротив, структура данных с отложенным вычислением остается «холодной» до тех пор, пока она не будет запущена, как и функция. Мы рассмотрим пример использования Fluture:

const Future = require('fluture');
const f = Future((reject, resolve) => {
  console.log('Hello world!');
  resolve(true);
});

Когда мы выполняем этот код, ничего не происходит - структура Future создается, но никогда не используется. Только когда мы добавим f.fork(console.error, console.log), мы увидим наше сообщение «Hello world». Это означает, что тот, кто дал нам будущее, не только дал нам контроль над что происходит с результатом его операции, но также и над , если и когда операция произойдет. . Когда я упомянул «контроль побочных эффектов», я имел в виду именно это.

Конечно, отмена может быть хорошей функцией, но я никогда не находил ситуации, когда мне нужно было конкретное состояние гонки.

В каждой ситуации, когда вас интересует первый результат нескольких промисов, это достаточно распространенный вариант использования, чтобы Promise.race можно было включить в собственные промисы. Тривиальным примером может служить тайм-аут запроса:

const getBob = () => Promise.race([
  get('/users/bob'),
  new Promise((l, r) => setTimeout(r, 20000, 'request timeout'))
]);

С Promises тайм-аут всегда будет занимать цикл событий на 20 секунд - даже если запрос get разрешится через 1 мс. Это означает, что если вы выполняете много одновременных запросов, вы без нужды расходуете много ресурсов. Если Promises будет поддерживать отмену, тайм-аут будет сброшен, как только станет известен победитель гонки.

обработка исключений против ожидаемых ошибок, и для меня это немного пахнет Java

Вау, вау! Это было неуместно.

Если я хочу получить ожидаемые ошибки, вместо того, чтобы генерировать исключение (что для меня так же плохо, как break on loops или goto), я просто возвращаю флаг для своего результата

Ага! Мы находимся в простом недоразумении. Похоже, вы нашли свой способ отделить ожидаемые сбои от исключений. Я вижу в этом подкрепление: обещания сочетают ожидаемые сбои с исключениями, что вынуждает вас, как пользователя, использовать другой механизм для обработки ожидаемых сбоев. Ветвь отклонения обещания была бы идеальным местом для размещения вашего помеченного результата, потому что она позволяет вам восстановить его на более позднем этапе, но потому что ветвь отклонения также может содержат исключения, вы больше не можете его использовать. Теперь вы, вероятно, сделаете что-то вроде:

getUser().then(user => {
  if(user.isFlagged) return user; //<- custom control flow
  return doThingsWithUser(user);
}).then(result => {
  if(result.isFlagged) return handleFailure(result);
  return handleSuccess(result)
}, err => handleException(err));

В этом настраиваемом фрагменте потока управления не было бы необходимости, если бы не промисы, перехватывающие исключения. С Fluture:

getUser()
.chain(doThingsWithUser)
.fork(handleFailure, handleSuccess)
process.on('uncaughtException', handleException)

Я никогда не обнаруживал, что создаю объект с помощью метода then

Представьте, что у вас есть массив моделей User, которые выглядят примерно так:

const User = ({handle, firstName, lastName}) => ({
  handle,
  firstName,
  lastName,
  getName: () => `${firstName} ${lastName}`
});
const getUsers = () => Promise.resolve([
  User({
    handle: 'avaq',
    firstName: 'Aldwin',
    lastName: 'Vlasblom'
  }),
  ...
]);

Конечно, массив пользователей является результатом чтения из базы данных или получения из API, причем дело в том, что оно прибывает внутри обещания.

Теперь нам нужно, чтобы функция getNameByHandle вычисляла полное имя по дескриптору. Чтобы сделать это быстро, мы сначала визуализируем StrMap геттеров, используя Lodash keyBy и mapValues:

getUsers()
.then(users => _.mapValues(_.keyBy(users, 'handle'), 'getName'))
.then(index => {
  const getNameByHandle = handle => index[handle]();
  //Here we would do more with the function we created.
  //For now I'll leave it at an example.
  return getNameByHandle('avaq');
})
.then(console.log, console.error);

Хотя этот код надуманный, он не такой уж безумный и работает нормально, пока кто-то не зарегистрируется в нашем сервисе, используя then в качестве дескриптора. В этот момент в строке 2 мы вернем объект с функцией then. Promise будет думать, что это Promise, и попытается его использовать, программа никогда не дойдет до строки 3.

Я пытаюсь проиллюстрировать, что Promises сделали опасной передачу сопоставлений из строки в функцию по конвейеру. Если вы вспомните все программы, которые вы написали с помощью Promises, можете ли вы гарантировать, что никогда не передавали объект функций, в котором (один из) ключи были созданы в результате ввода данных пользователем? Даже если вы этого не сделали, это нужно принять во внимание - одна из этих тонких вещей, о которых вы должны помнить при использовании Promise.

То же самое происходит с другими специальными методами, такими как toString или toJSON.

У них одни и те же проблемы, хотя разница в том, что toString и toJSON не лежат в основе наших программ.

большинство программистов, которые заявляли о обещаниях, мало что знали о них

Это как раз и есть проблема. Многие проблемы с обещаниями просто не обсуждаются, большинство статей, восхваляющих обещания, пропускают их. Я надеюсь пролить свет на эти проблемы.

Я не говорю, что у вас недостаточно знаний об обещаниях

Я использую Promise шесть лет, с тех пор, как их первые ошибочные реализации появились в jQuery. Раньше я вносил небольшой кусочек в Bluebird, и я создал свою собственную, полностью совместимую со спецификациями реализацию Promises / A +. Тем не менее, я надеюсь, что одних моих рассуждений достаточно, чтобы убедить вас в моих аргументах.