Сохранение читаемых цепочек

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

myArray.map(x => convertX)
  .filter()
  .whatever()
  .etc()

Это невероятно легко читать. Однако, когда я создаю цепочки обещаний с пользовательскими функциями, он становится намного более беспорядочным.

database.query(first query)
  .then(results => {
    // do stuff
    // do more
    // even more
    return database.query(second query)
  })
  .then(results => {
    // rinse and repeat
  })
  .catch(err => { 
    // error handling 
  })

Теперь это может быть разборчивым, но когда цепочка обещаний распространяется дальше, она становится немного. Если я сделаю каждое обещание своей собственной функцией, тогда я могу оптимизировать процесс, чтобы код выглядел примерно так (что imo, на 1000 раз больше разборчиво).

db.query(first query)
  .then(storeFirstQuery)
  .then(secondQueryAndStoreIt)
  .then(thirdQueryAndStoreIt)
  .catch(errHandlingFunction)

Таким образом, я могу изменить функции без необходимости манипулировать значениями, которые передаются от одного обещания к следующему. Если обещание использует результат другого, оно должно быть только после другого, но не сразу после. Таким образом я могу прокрасться promises туда, где мне нужно.

Однако для этого требуется, чтобы моя цепочка обещаний использовала переменные за пределами каждой области обещания. Есть ли проверенный и надежный способ сделать это?

Изменить - Кажется, что async/await - лучший способ сделать это, но я запускаю Node на Heroku, и он еще не поддерживается:/

Ответ 1

Ну, вы можете использовать что-то вроде этого с promises:

myArray.map(x => convertX)
  .filter()
  .whatever()
  .etc()

если вы используете мой rsp модуль из числа npm.

Кроме того, вы можете использовать функции async/await ES2017 для упрощения цепей promises, особенно их области.

Потому что с таким кодом:

db.query(first query)
  .then(storeFirstQuery)
  .then(secondQueryAndStoreIt)
  .then(thirdQueryAndStoreIt)
  .catch(errHandlingFunction)

если вам нужно использовать результат первого запроса в последнем обработчике thirdQueryAndStoreIt(), у вас возникла проблема с доступом к данным, выходящим за рамки. Но когда вы это сделаете:

try {
    let a = await db.query(first query);
    let b = await storeFirstQuery();
    let c = await secondQueryAndStoreIt();
    let d = await thirdQueryAndStoreIt(a); // use 'a' here
} catch (e) {
    errHandlingFunction(e);
}

тогда у вас нет проблемы с областью видимости, так как вы можете легко получить доступ ко всем ранее назначенным переменным.

См. это для версий Node, поддерживающих этот синтаксис:

Вы можете использовать его с Node v7.6 + из коробки или с помощью Node v7.0 + с флагом --harmony.

Для более старых версий Node вы можете использовать co или Bluebird.coroutine для аналогичного синтаксиса с использованием функций генератора и yield вместо await.

Ответ 2

если вы действительно этого хотите, вы можете ограничить область применения одним мета-обещанием, создав его самостоятельно:

return new Promise((resolve, reject) => {
  const f1 = () => { /* ... */ };
  const f2 = () => { /* ... */ };
  const f3 = () => { /* ... */ };

  return db.query()
    .then(f1)
    .then(f2)
    .then(f3)
    .then(resolve)
    .catch(reject);
});

но самый читаемый способ сделать это - использовать async/await.