Promisekit: Promises array chaining feature request

Created on 3 Sep 2019  ·  16Comments  ·  Source: mxcl/PromiseKit

  • PromiseKit version I'm using: 6.8.4
  • PromiseKit installed with CocoaPods

It would be nice to have a feature to automatically chain an array of promises executing them serially one after the other, alternatively to when(fulfilled:) or when(resolved:) that execute in parallel the promises. A code example has been written on #662, but in my opinion it should be integrated into the library.

BTW I had the same misunderstanding about the expected behaviour of when(fulfilled: array.makeIterator(), concurrently: 1)

Most helpful comment

I was referring to the last snippet I wrote in this thread:

func createPromise(msg: String) -> Promise<Void> {
    return after(seconds: 10).done {
        print(msg)
    }
}

var foo = Promise()
for i in 1...10 {
    foo = foo.then { createPromise(msg: "P\(i)") }
}
foo.done {
    print("Finish")
}

Maybe the difference is not clear? Instead of creating all 10 promises at once, you chain the creation of the promises 10 times, this ensures that you are not defeated by the unreliable nature of asynchronous code.

All 16 comments

662 is not the right ticket I think.

BTW I had the same misunderstanding about the expected behaviour of when(fulfilled: array.makeIterator(), concurrently: 1)

What misunderstanding? This does what you want.

662 is not the right ticket I think.

My fault, it was the #622.

What misunderstanding?

I misunderstood the behaviour of the function just like @kubajakowski reported in his issue.

This does what you want.

Could you be so kind to post a code example?

The issue you link involves a misunderstanding in how promises work, not a misunderstanding of the when function.

There is an example on how to use when concurrently in the ticket you linked.

There is also an example of how to chain an array of promises in our documentation.

I agree with you, I think I'm misunderstanding how promises work.

If you mean the following example, could you be so kind to add also how arrayOfPromises should be initialised, because when I try to run on my environment I don't get the result I'm expecting.

var foo = Promise()
for nextPromise in arrayOfPromises {
    foo = foo.then { nextPromise }
}
foo.done {
    // finish
}

Many thanks for the support!

I don't get the result I'm expecting.

I need more information in order to be helpful. What happens and what are you expecting.

Please try to have a look at this full example:

import UIKit
import PromiseKit

func createPromise(msg: String) -> Promise<Void> {
    return after(seconds: 10).then { _ -> Promise<Void> in
        print(msg)
        return Promise.value(())
    }
}

var arrayOfPromises: [Promise<Void>] = []
for i in 1...10 {
    arrayOfPromises.append(createPromise(msg: "P\(i)"))
}

var foo = Promise()
for nextPromise in arrayOfPromises {
    foo = foo.then { nextPromise }
}
foo.done {
    // finish
    print("Finish")
}

What I would like to see is the following result:

P2
P3
P4
P5
P6
P7
P8
P9
P10
Finish

Where the messages are printed one after the other every 10 seconds (more or less): given 10 messages x 10 seconds, the code should finish after 100 seconds. Instead the result is that the are all printed out after only 10 seconds, sometimes in a random order (see an example below)

P5
P3
P4
P6
P2
P7
P8
P10
P9
Finish

I hope that you can better understand now what I would like to achieve with PromiseKit.

This is because you are creating the promises all at once. Promises “start” as soon as you create them. See our FAQ for more details.

Instead you need to create the promises in the then.

func createPromise(msg: String) -> Promise<Void> {
    return after(seconds: 10).done {
        print(msg)
    }
}

var foo = Promise()
for i in 1...10 {
    foo = foo.then { createPromise(msg: "P\(i)") }
}
foo.done {
    print("Finish")
}

Or use when(concurrently).

Yes I read the FAQ, but still I don't know how to use PromiseKit to implement what I'm trying to achieve. Is it feasible (if yes, please could you be so kind to write a very simple working snippet?) or in your opinion using PromiseKit for this scope is not the right thing?

I gave you a snippet in may previous comment. That does what you want. Thanks.

Maybe I'm doing something wrong, but I updated the example with your suggestion and still it doesn't do what I want. I'm kindly asking again, could you be so kind to write down a very simple working example?

import UIKit
import PromiseKit

func createPromise(msg: String) -> Promise<Void> {
    return after(seconds: 10).then { _ -> Promise<Void> in
        print(msg)
        return Promise.value(())
    }
}

var arrayOfPromises: [Promise<Void>] = []
for i in 1...10 {
    arrayOfPromises.append(createPromise(msg: "P\(i)"))
}

when(fulfilled: arrayOfPromises.makeIterator(), concurrently: 1).done { _ in
    // finish
    print("Finish")
}

You didn't use my snippet, you adapted it. The snippet I provided works, thanks.

I'm sorry but if by your snippet you mean this:

when(fulfilled: array.makeIterator(), concurrently: 1)

I don't have a complete picture how I should run it, it needed at least some initialization (what's inside array? how should initialise it?). If you mean another snippet in another ticket, please be more specific.

I don't know what is wrong with my question, I just asked for a simple, but complete and working example. If you have any problem to provide it just close the ticket, I don't want to waste any more of your precious time.

I was referring to the last snippet I wrote in this thread:

func createPromise(msg: String) -> Promise<Void> {
    return after(seconds: 10).done {
        print(msg)
    }
}

var foo = Promise()
for i in 1...10 {
    foo = foo.then { createPromise(msg: "P\(i)") }
}
foo.done {
    print("Finish")
}

Maybe the difference is not clear? Instead of creating all 10 promises at once, you chain the creation of the promises 10 times, this ensures that you are not defeated by the unreliable nature of asynchronous code.

Thank you for clarifying the misunderstanding on my side. I appreciate it!

I confirm that the sample also work on my side.

Said that, reproducing a similar behaviour with an array of promises and when(fulfilled: array.makeIterator(), concurrently: 1) function it's not possible, correct? (because as soon as I create an array of promises they automatically start the execution in parallel)

Said that, reproducing a similar behaviour with an array of promises and when(fulfilled: array.makeIterator(), concurrently: 1) function it's not possible, correct? (because as soon as I create an array of promises they automatically start the execution in parallel)

It will work with when(fulfilled:concurrently:) since you provide it an iterator that creates the promises on-demand.

OK I think I got it, something like this:

var intGenerator = (1...10).makeIterator()
var current = 0

func log(_ msg: String) {
    let ts = Date().description
    print("\(ts): \(msg)")
}

let generator = AnyIterator<Promise<Void>> {
    guard let i = intGenerator.next() else {
        return nil
    }
    log("Doing \(i)")
    return after(.seconds(1)).done {
        log("Done \(i)")
        current = i
    }
}

log("Start")
when(fulfilled: generator, concurrently: 1).done { _ in
    log("Finish")
}

Thank you again for the explanation, I think I got the concept finally 🤦‍♂

Was this page helpful?
0 / 5 - 0 ratings