1

Now I am migrating from PromiseKit to Concurrency. As I understood, I should replace Promise and Guarantee with Task. However, I cannot find a replacement for Promise<T>.pending(). Is there something similar in Concurrency?

For instance, I want to use Task in the code below:

import CoreLocation
import PromiseKit

class A: NSObject {
    let locationManager = CLLocationManager()
    let promiseAndResolver = Promise<CLLocation>.pending()
    
    func f() {
        locationManager.delegate = self
        locationManager.requestLocation()
    }
}

extension A: CLLocationManagerDelegate {
    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        if let location = locations.first {
            promiseAndResolver.resolver.fulfill(location)
        }
    }
    
    func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
        promiseAndResolver.resolver.reject(error)
    }
}

class B {
    let a = A()
    
    func f() {
        a.promiseAndResolver.promise.done {
            print($0)
        }.cauterize()
        a.f()
    }
}

let b = B()
b.f()
Roman Podymov
  • 4,168
  • 4
  • 30
  • 57

1 Answers1

2

You don't need to use Task to do this. You can use withCheckedThrowingContinuation to convert the method f into an async throws method. Then when you call it, a child task of the current task is automatically created and you can await it directly.

class A: NSObject, CLLocationManagerDelegate {
    let locationManager = CLLocationManager()
    var continuation: CheckedContinuation<CLLocation, Error>?
    func f() async throws -> CLLocation {
        try await withCheckedThrowingContinuation { continuation in
            self.continuation = continuation
            locationManager.delegate = self
            locationManager.requestLocation()
        }
    }

    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        continuation?.resume(returning: locations[0])
    }
    
    func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
        continuation?.resume(throwing: error)
    }
}

Your B.f method can also be rewritten as an async method (I don't know what cauterize does):

class B {
    let a = A()
    
    func f() async {
        do {
            print(try await a.f())
        } catch { 
            print(error) 
        }
    }
}

let b = B()

_runAsyncMain { // so that we have an async context to await tasks
    await b.f()
}

Side note: if you actually run this, you will get an error printed from the catch block, probably because location manager needs permission :)

Sweeper
  • 213,210
  • 22
  • 193
  • 313