I'm using PromiseKit with Swift, which has been very handy so far. One of the functions they provide is when()
, which allows you to have an array of any number of promises and execute something only once ALL of them have completed.
However, the promises in the array are executed in parallel. I have not found any function that allows me to execute them sequentially. I've tried to write a recursive function of my own, but it doesn't appear to be executing the promises in the order in which they are in the array, and I get the occasional "Promise deallocated" bug. Please help!
static func executeSequentially(promises: [Promise<Void>]) -> Promise<Void> {
return Promise<Void> { fulfil, reject in
var mutablePromises = promises
if mutablePromises.count > 0 {
mutablePromises.first!
.then { _ -> Promise<Void> in
print("DEBUG: \(mutablePromises.count) promises in promise array.")
mutablePromises.remove(at: 0)
return executeSequentially(promises: mutablePromises)
}.catch { error in
print("DEBUG: Promise chain rejected.")
reject(error)
}
} else {
print("DEBUG: Promise chain fulfilled.")
fulfil(())
}
}
}
Here's an extension that takes an array of promises and returns a new promise with all the individual promises chained together to run serially. I've modified the version found here to work with Swift 5 / PromiseKit 7 alpha: https://gist.github.com/kashifshaikh/416b9ffbd300eb680fad3641b6ec53ea
The accompanying post from the original author can be found here: https://medium.com/@peatiscoding/promisekit-chaining-3c957a8ace24
import Foundation
import PromiseKit
extension Promise {
/// Returns a single Promise that you can chain to. Wraps the chains of promises passed into the array into a serial promise to execute one after another using `promise1.then { promise2 }.then ...`
///
/// - Parameter promisesToExecuteSerially: promises to stitch together with `.then` and execute serially
/// - Returns: returns an array of results from all promises
public static func chainSerially<T>(_ promises:[() -> Promise<T>]) -> Promise<[T]> {
// Return a single promise that is fulfilled when
// all passed promises in the array are fulfilled serially
return Promise<[T]> { seal in
var outResults = [T]()
if promises.count == 0 {
seal.fulfill(outResults)
} else {
let finalPromise:Promise<T>? = promises.reduce(nil) { (r, n) -> Promise<T> in
return r?.then { promiseResult -> Promise<T> in
outResults.append(promiseResult)
return n()
} ?? n()
}
finalPromise?.done { result in
outResults.append(result)
seal.fulfill(outResults)
}.catch { error in
seal.reject(error)
}
}
}
}
}
Usage:
let serialSavePromises: [() -> Promise<Bool>] = allImages.map { image in
return { [weak self] in
guard let self = self else { return .value(false) }
return self.saveImage(image)
}
}
return Promise<[Bool]>.chainSerially(serialSavePromises)
You can use when(fulfilled: array.makeIterator(), concurrently: 1)
.
example:
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")
}
source: https://github.com/mxcl/PromiseKit/issues/1093
And I simply encapsulated it.
func chainSerially<T, S>(_ datas: [T],_ iteratorHandle: @escaping (T) -> Promise<S>?) -> Promise<[S]> {
var generator = datas.makeIterator()
let iterator = AnyIterator<Promise<S>> {
guard let next = generator.next() else {
return nil
}
return iteratorHandle(next)
}
return when(fulfilled: iterator, concurrently: 1)
}
use:
func log(_ msg: String) {
let ts = Date().description
print("\(ts): \(msg)")
}
func test() {
log("start")
chainSerially(Array(1...10)) { (value: Int?) -> Promise<Void>? in
guard let value = value else {return nil}
log("Doing \(value)")
return after(.seconds(1)).done {
log("done \(value)")
}
}.done { _ in
print("end")
}
}
logs:
2021-10-09 02:01:43 +0000: start
2021-10-09 02:01:43 +0000: Doing 1
2021-10-09 02:01:44 +0000: done 1
2021-10-09 02:01:44 +0000: Doing 2
2021-10-09 02:01:45 +0000: done 2
2021-10-09 02:01:45 +0000: Doing 3
2021-10-09 02:01:46 +0000: done 3
2021-10-09 02:01:46 +0000: Doing 4
2021-10-09 02:01:47 +0000: done 4
2021-10-09 02:01:47 +0000: Doing 5
2021-10-09 02:01:48 +0000: done 5
2021-10-09 02:01:48 +0000: Doing 6
2021-10-09 02:01:50 +0000: done 6
2021-10-09 02:01:50 +0000: Doing 7
2021-10-09 02:01:51 +0000: done 7
2021-10-09 02:01:51 +0000: Doing 8
2021-10-09 02:01:52 +0000: done 8
2021-10-09 02:01:52 +0000: Doing 9
2021-10-09 02:01:53 +0000: done 9
2021-10-09 02:01:53 +0000: Doing 10
2021-10-09 02:01:54 +0000: done 10
end
If you want serial, concurrently must be 1
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With