Hi all, I'm reading that when I create a Task, it is automatically started. What if I want to defer it until needed?
Prior to Swift Concurrency, my project was using PromiseKit to do such a thing. A Promise<T>
stores a bunch operation which can either return T
or "throw" an error, but does not execute the operation immediately until it's resolved. I can do something like this:
let promise = Promise<DataModel>(resolver: { seal in
do {
try DataService.requestServerForALargeJSON { json in
do {
let model = try DataModel.parseJSON(json)
seal.fulfill(model)
} catch {
seal.reject(JSONSerializationError.badJSON)
}
}
} catch {
seal.reject(NetworkRequestError.failure)
}
})
I wrapped a time consuming logic into a Promise object, but I don't need to execute it now. When the user triggers a specific UI action, I then "start" the promise:
promise.map { model in
// success
}.catch { error in
// error
}.finally {
// always executed at the end
}
This is like creating a Task but lazily executing it afterwards. What's more, PromiseKit enables me to do more complex things about multiple promises like:
let promises = [promise1, promise2, promise3]
// some time afterwards
when(fulfilled: promises).then { results in
// ...
}.catch { error in
switch error {
case MyError.case1:
//…
case MyError.case2:
//…
}
}
This is like creating a group of Task's but lazily execute them and handle when they all succeeded or when one of them failed.
Since I have migrated my networking module to use Swift Concurrency, I cannot write like this:
let promise = Promise<DataModel>(resolver: { seal in
do {
let json = try await DataService.aLargeJSONFromServer()
let model = try await DataModel.asyncParseJSON(json)
seal.fulfill(model)
} catch {
seal.reject(error)
}
})
because I can't call asynchronous code from a synchronous context (the resolver closure). I can create the Task inside the closure but I still have to write the map
, catch
and finally
which I believe still falls into the category of old-fashioned "completionHandler".
I wish I could do something like this:
// this asyncTask is not started immediately
let asyncTask = AsyncThrowingTask<DataModel> {
let json = try await DataService.aLargeJSONFromServer()
let model = try await DataModel.asyncParseJSON(json)
return model
}
// later on...
do {
let model = try await asyncTask.start()
} catch {
// process error
}
Could anyone help me? I would like to eventually prune PromiseKit from my project, if possible.