1

Question: How to chain getNextPaymentPayload/makePayment/confirmPayment into a loop? You always can check if any payments remains with hasAnyPayments method.

Problem: You can't do payments in parallel. To make a payment you need to wait while the previous one will be completed.

Example:

SomeService
    //first payment
    .getNextPaymentPayload()
    .then(paymentPayload => this.makePayment(paymentPayload))
    .then(paymentResponse => this.confirmPayment)
    //second payment        
    .then(SomeService.getNextPaymentPayload())
    .then(paymentPayload => this.makePayment(paymentPayload))
    .then(paymentResponse => this.confirmPayment)
    //so on...

VB_
  • 45,112
  • 42
  • 145
  • 293
  • what does `hasAnyPayments` return? – Alnitak Dec 10 '15 at 15:30
  • `.then(getNextPaymentPayload)`? – elclanrs Dec 10 '15 at 15:31
  • @Alnitak `true` or `false`. If `false` - you should stop payments, if `yes` - you should make at least one more round of `getNextPaymentPayload/makePayment/confirmPayment` – VB_ Dec 10 '15 at 15:32
  • @elclanrs you're right. Corrected – VB_ Dec 10 '15 at 15:32
  • So `hasAnyPayments` is synchronous, or returns a promise that returns a boolean? – Alnitak Dec 10 '15 at 15:34
  • But you are calling the function now, that looks wrong. `.then` is a higher-order function, it expects a function. `.then` sequences things, so if `getNextPaymentPayload` returns a promise, `.then(getNextPaymentPayload)` should happen after the previous one in the chain. – elclanrs Dec 10 '15 at 15:35
  • Use a [recursive approach](http://stackoverflow.com/a/29396005/1048572) – Bergi Dec 10 '15 at 15:53
  • You are looking for a QueueConsumer that performs each registered task sequentially... right? – Hitmands Dec 10 '15 at 18:22

1 Answers1

3

Assuming that hasAnyPayments will return a Promise that eventually resolves to a boolean:

let doPayments = () => {
    return hasAnyPayments().then((hasAny) => {
        if (!hasAny) {   
            return Promise.resolve();        // all done
        } else {
            return SomeService.getNextPaymentPayload()
                      .then(paymentPayLoad => this.makePayment(paymentPayLoad))
                      .then(paymentResponse => this.confirmPayment(paymentResponse))
                      .then(doPayments);     // "recurse"
        }
    }
}     
Alnitak
  • 334,560
  • 70
  • 407
  • 495
  • seems like the most straightforward answer – Cameron Dec 10 '15 at 15:37
  • @bonebrigade I've been using code just like this myself, recently. In my own code I want a 1 second delay between each recursion so I use: `return new Promise((resolve) => setTimeout(resolve, 1000)` instead of a straight `Promise.resolve()`. – Alnitak Dec 10 '15 at 15:40
  • You could use [`Promise#delay`](http://bluebirdjs.com/docs/api/delay.html) in Bluebird. – sdgluck Dec 10 '15 at 16:11
  • @sdgluck I don't actually use Bluebird - the answer above just uses standard Promises without any extensions. – Alnitak Dec 10 '15 at 17:01
  • You don't need `return Promise.resolve()`. You can just do `return;` there. No need to create that extra promise. – jfriend00 Dec 11 '15 at 22:58
  • @jfriend00 because it's wrapped in a `.then()` that will convert the bare (undefined) return value into a resolved promise? You're right that I do need a `return` on the first line, though. – Alnitak Dec 13 '15 at 09:44
  • @Alnitak - I wasn't sure if the first `return` was needed or not since arrow functions do sometimes return things automatically (haven't quite figured out all their rules for that). – jfriend00 Dec 13 '15 at 16:05
  • @jfriend00 arrow functions only return automatically if they're a single expression without a `{ }` block. – Alnitak Dec 14 '15 at 08:09