You can use when or join to start something after multiple other promises have completed. The difference is in how they handled failed promises. It sounds like you want join. Here is a concrete, though simple example.
This first block of code is an example of how to create 2 promise chains and then wait for both of them to complete before starting the next task. The actual work being done is abstracted away into some functions. Focus on this block of code as it contains all the conceptual information you need.
Snippet
let chain1 = firstly(execute: { () -> (Promise<String>, Promise<String>) in
    let secondPieceOfInformation = "otherInfo" // This static data is for demonstration only
    // Pass 2 promises, now the next `then` block will be called when both are fulfilled
    // Promise initialized with values are already fulfilled, so the effect is identical
    // to just returning the single promise, you can do a tuple of up to 5 promises/values
    return (fetchUserData(), Promise(value: secondPieceOfInformation))
}).then { (result: String, secondResult: String) -> Promise<String> in
    self.fetchUpdatedUserImage()
}
let chain2 = firstly {
    fetchNewsFeed() //This promise returns an array
}.then { (result: [String : Any]) -> Promise<String> in
    for (key, value) in result {
        print("\(key) \(value)")
    }
    // now `result` is a collection
    return self.fetchFeedItemHeroImages()
}
join(chain1, chain2).always {
    // You can use `always` if you don't care about the earlier values
    let methodFinish = Date()
    let executionTime = methodFinish.timeIntervalSince(self.methodStart)
    print(String(format: "All promises finished %.2f seconds later", executionTime))
}
PromiseKit uses closures to provide it's API. Closures have an scope just like an if statement. If you define a value within an if statement's scope, then you won't be able to access it outside of that scope.
You have several options to passing multiple pieces of data to the next then block.
- Use a variable that shares a scope with all of the promises (you'll likely want to avoid this as it works against you in managing the flow of asynchronous data propagation)
 
- Use a custom data type to hold both (or more) values. This can be a tuple, struct, class, or enum.
 
- Use a collection (such as a dictionary), example in 
chain2 
- Return a tuple of promises, example included in 
chain1 
You'll need to use your best judgement when choosing your method.
Complete Code
import UIKit
import PromiseKit
class ViewController: UIViewController {
    let methodStart = Date()
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        <<Insert The Other Code Snippet Here To Complete The Code>>
        // I'll also mention that `join` is being deprecated in PromiseKit
        // It provides `when(resolved:)`, which acts just like `join` and
        // `when(fulfilled:)` which fails as soon as any of the promises fail
        when(resolved: chain1, chain2).then { (results) -> Promise<String> in
            for case .fulfilled(let value) in results {
                // These promises succeeded, and the values will be what is return from
                // the last promises in chain1 and chain2
                print("Promise value is: \(value)")
            }
            for case .rejected(let error) in results {
                // These promises failed
                print("Promise value is: \(error)")
            }
            return Promise(value: "finished")
            }.catch { error in
                // With the caveat that `when` never rejects
        }
    }
    func fetchUserData() -> Promise<String> {
        let promise = Promise<String> { (fulfill, reject) in
            // These dispatch queue delays are standins for your long-running asynchronous tasks
            // They might be network calls, or batch file processing, etc
            // So, they're just here to provide a concise, illustrative, working example
            DispatchQueue.global().asyncAfter(deadline: .now() + 2.0) {
                let methodFinish = Date()
                let executionTime = methodFinish.timeIntervalSince(self.methodStart)
                print(String(format: "promise1 %.2f seconds later", executionTime))
                fulfill("promise1")
            }
        }
        return promise
    }
    func fetchUpdatedUserImage() -> Promise<String> {
        let promise = Promise<String> { (fulfill, reject) in
            DispatchQueue.global().asyncAfter(deadline: .now() + 2.0) {
                let methodFinish = Date()
                let executionTime = methodFinish.timeIntervalSince(self.methodStart)
                print(String(format: "promise2 %.2f seconds later", executionTime))
                fulfill("promise2")
            }
        }
        return promise
    }
    func fetchNewsFeed() -> Promise<[String : Any]> {
        let promise = Promise<[String : Any]> { (fulfill, reject) in
            DispatchQueue.global().asyncAfter(deadline: .now() + 1.0) {
                let methodFinish = Date()
                let executionTime = methodFinish.timeIntervalSince(self.methodStart)
                print(String(format: "promise3 %.2f seconds later", executionTime))
                fulfill(["key1" : Date(),
                         "array" : ["my", "array"]])
            }
        }
        return promise
    }
    func fetchFeedItemHeroImages() -> Promise<String> {
        let promise = Promise<String> { (fulfill, reject) in
            DispatchQueue.global().asyncAfter(deadline: .now() + 2.0) {
                let methodFinish = Date()
                let executionTime = methodFinish.timeIntervalSince(self.methodStart)
                print(String(format: "promise4 %.2f seconds later", executionTime))
                fulfill("promise4")
            }
        }
        return promise
    }
}
Output
promise3 1.05 seconds later
  array ["my", "array"]
  key1 2017-07-18 13:52:06 +0000
  promise1 2.04 seconds later
  promise4 3.22 seconds later
  promise2 4.04 seconds later
  All promises finished 4.04 seconds later
  Promise value is: promise2
  Promise value is: promise4