1

PromiseKit has this initializer for Promise:

/// Initialize a new promise that can be resolved with the provided `Resolver`.
public init(resolver body: (PromiseKit.Resolver<T>) throws -> Void)

Sometimes I need my function to return a Promise but the return value of that function depends on other throwing functions. Is it valid to call try directly without a do/catch block since the resolver closure throws? Or must I wrap each throwing function in do and in the catch call seal.reject(error)?

I would prefer to directly use try because it is much cleaner to return the end result without the do/catch.

This issue seems relevant, but I'd like some confirmation on these approaches since it isn't called out in the docs: https://github.com/mxcl/PromiseKit/issues/799.

Is this valid?

  private func getCNContacts() -> Promise<[CNContact]> {
    return Promise { seal in
      let fullNameKeyDescriptor = CNContactFormatter.descriptorForRequiredKeys(for: .fullName)
      guard let keysToFetch = [fullNameKeyDescriptor, CNContactPhoneNumbersKey] as? [CNKeyDescriptor] else {
        throw CKPersistenceError.failedToFetch("Could not cast as [CNKeyDescriptor]")
      }

      var results: [CNContact] = []
      let fetchRequest = CNContactFetchRequest(keysToFetch: keysToFetch)
      try contactStore.enumerateContacts(with: fetchRequest) { contact, _ in
        results.append(contact)
      }

      seal.fulfill(results)
    }
  }

Or must I use do/catch like this:

private func getCNContacts() -> Promise<[CNContact]> {
    return Promise { seal in
      let fullNameKeyDescriptor = CNContactFormatter.descriptorForRequiredKeys(for: .fullName)
      guard let keysToFetch = [fullNameKeyDescriptor, CNContactPhoneNumbersKey] as? [CNKeyDescriptor] else {
        seal.reject(CKPersistenceError.failedToFetch("Could not cast as [CNKeyDescriptor]"))
        return
      }

      do {
        var results: [CNContact] = []
        let fetchRequest = CNContactFetchRequest(keysToFetch: keysToFetch)
        try contactStore.enumerateContacts(with: fetchRequest) { contact, _ in
          results.append(contact)
        }
        seal.fulfill(results)
      } catch {
        seal.reject(error)
      }
    }
  }

There's also this possibility, but I think it might have threading implications:

  private func getCNContacts() -> Promise<[CNContact]> {
    let fullNameKeyDescriptor = CNContactFormatter.descriptorForRequiredKeys(for: .fullName)
    guard let keysToFetch = [fullNameKeyDescriptor, CNContactPhoneNumbersKey] as? [CNKeyDescriptor] else {
      let error = CKPersistenceError.failedToFetch("Could not cast as [CNKeyDescriptor]")
      return Promise(error: error)
    }

    do {
      var results: [CNContact] = []
      let fetchRequest = CNContactFetchRequest(keysToFetch: keysToFetch)
      try contactStore.enumerateContacts(with: fetchRequest) { contact, _ in
        results.append(contact)
      }
      return Promise.value(results)
    } catch {
      return Promise(error: error)
    }
  }
blwinters
  • 1,911
  • 19
  • 40

1 Answers1

2

It is perfectly valid to have have throwing function inside Promise and not handle the error within the promise resolution block.

But, if you want to handle the error you could use catch(on: flags:, policy: , _ ),

Here is small experiment that I did based on your question.

    simplePromise =  Promise { resolver in
        let string = try self.getSomeString()
        resolver.resolve(Result.fulfilled(string))
    }

    simplePromise.done { results in
        print(results)
    }.catch { error in
        print("Error occurred: \(error)")
    }

Now, if my method getSomeString() throws, the code inside catch block is called but if it is resolved successfully, then code inside done is called.

You could try out by creating the method getSomeString and throwing from there like so,

enum MyError: Error {
    case sampleError
}

func getSomeString() throws -> String {
    throw MyError.sampleError
}

And, also try out by not throwing.

So, it seems like usual way to handle the error of throwing function is implementing catch callback.

You could also not implement catch yourself and your code would not crash. In the end the throwing functions are treated as sealed.reject

By the way, if you wish you can look at the code. You dont have to handle the error with do { } catch but PromiseKit does it internally, look here for your reference.

Sandeep
  • 20,908
  • 7
  • 66
  • 106