Могу ли я сломать цепочку раньше с помощью bluebird Promises?

Я не обязательно хочу ошибки, но у меня есть:

getFromDb().then (tradeData) ->
  if not tradeData
    # DO NOT CONTINUE THE CHAIN
  else
    getLatestPrice tradeData
.then (latestPrice) ->
  ...
.then ->
  ...
.then ->
  ...
.catch (err) ->
  next err

Любой способ для меня прервать цепочку, если нет tradeData?

Ответ 1

getFromDb().then (tradeData) ->
  if tradeData
    getLatestPrice tradeData ->
      .then (latestPrice) ->
        ...
      .then ->
        ...
      .then ->
        ...
      .catch (err) ->
        next err
  else
    getSomethingElse ->
       send("no data")

В версии 3.0 вы сможете это сделать:

p = getFromDb().then (tradeData) ->
  if not tradeData
    send("no data");
    p.break()
  else
    getLatestPrice tradeData
.then (latestPrice) ->
  ...
.then ->
  ...
.then ->
  ...
.catch (err) ->
  next err

Ответ 2

Несмотря на принятый ответ, но я хотел бы сказать всем искателям, что функция "break()" была изменена на "cancel()"

Используйте что-то вроде этого:

p = getFromDb().then (tradeData) ->
  if not tradeData
    send("no data");
    p.cancel(); // Look Here!!!!!!!!!!!!!!!!
  else
    getLatestPrice tradeData
.then (latestPrice) ->
  ...
.then ->
  ...
.then ->
  ...
.catch (err) ->
  next err

До этого обязательно добавьте следующие строки в config:

Promise.config({
    cancellation: true
});

Ответ 3

Мне просто интересно, почему бы не воспользоваться тем фактом, что вы можете throw все, что захотите, а не только что-то, что есть instanceof Error. Это считается плохой практикой? По-моему, это зависит от того, чего вы пытаетесь достичь. Цепочка обещаний может быть прервана по разным причинам, но в целом эти два будут разделены на две группы. Classic error occur и early break in chain. Логически второй нельзя считать чем-то, что должно быть instance of Error.

const handleError = (err) => {
  ...
}

const skip = (reason, ..., ...) => {
  /**
   * construct whatever you like
   * just for example here return reason
   */
  return reason
}

Promise.resolve()
.then(() => {
  if (iShouldEndChainEarlier) {
    throw skip('I would like to end chain earlier')
  }

  return asyncOperation1()
})
.then(results => {
  ...
  return asyncOperation2(results)
})
.then(... => {
  ...
})
.catch(interrupt => {
  if (interrupt instanceof Error) {
    return handleError(interrupt)
  }

  /**
   * Handle breaking promise chain earlier
   * having interrupt reason in scope
   */
})

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

Мы могли бы утверждать, что это можно считать чем-то против first error pattern в node. Если существует ошибка, лучшим способом было бы вызвать обратный вызов, например callback(err), где err действительно должен быть instanceof Error иначе callback(null, data). Но с другой стороны, имея в виду, что .catch(fn) для меня просто для сахара then(undefined, onRejected), он кажется достаточно хорошим для обработки параметра onRejected, основанного на ситуации, в которой вы находитесь.