我有 a,b,c,d,e 耗时的任务函数和完成处理程序。
它们之间存在约束:
如果没有任务 d ,我可以像swift一样编写代码(尚未测试)
let group = DispatchGroup()
group.enter()
a() { group.leave() }
group.wait()
group.enter()
b() { group.leave() }
group.enter()
c() { group.leave() }
group.notify(queue: .main) {
e()
}
如何在不等待 a 的情况下添加任务 d ?
于4月30日10:00(+8)编辑
代码不同表示
最简单的方法是使下载功能同步,并在其文档中添加一条警告,告知它不应该从主线程中调用。
所以我根据它制作了一个版本。这种方式无法处理并发调用的返回值。但它看起来真的像async / await。所以我现在很满意。谢谢你们。
async / await like part是
myQueue.async {
downloadSync("A")
downloadSync("B", isConcurrent: true)
downloadSync("C", isConcurrent: true)
downloadSync("D", 4, isConcurrent: true)
waitConcurrentJobs()
downloadSync("E")
}
完整的代码如下。
let myGroup = DispatchGroup()
let myQueue = DispatchQueue(label: "for Sync/Blocking version of async functions")
func waitConcurrentJobs() {
myGroup.wait()
}
// original function (async version, no source code)
func download(_ something: String, _ seconds: UInt32 = 1, completionHandler: @escaping ()->Void = {}) {
print("Downloading \(something)")
DispatchQueue.global().async {
sleep(seconds)
print("\(something) is downloaded")
completionHandler()
}
}
// wrapped function (synced version)
// Warning:
// It blocks current thead !!!
// Do not call it on main thread
func downloadSync(
_ something: String,
_ seconds: UInt32 = 1,
isConcurrent: Bool = false
){
myGroup.enter()
download(something, seconds) { myGroup.leave() }
if !isConcurrent {
myGroup.wait()
}
}
// Now it really looks like ES8 async/await
myQueue.async {
downloadSync("A")
downloadSync("B", isConcurrent: true)
downloadSync("C", isConcurrent: true)
downloadSync("D", 4, isConcurrent: true)
waitConcurrentJobs()
downloadSync("E")
}
结果
答案 0 :(得分:0)
编辑:最简单的方法是使download
函数同步,并在其文档中添加一条警告,告知它永远不应该从主线程调用。异步函数的厄运金字塔是coroutines were proposed的原因,除了斯莱夫特的创造者克里斯拉特纳之外。截至2018年4月,它尚未成为等待审核的正式提案,因此很有可能您无法在Swift 5中看到它。
同步下载功能:
// Never call this from main thread
func download(_ something: String, _ seconds: UInt32 = 1, completionHandler: @escaping ()->Void = {}) {
let group = DispatchGroup()
print("Downloading \(something)")
group.enter()
DispatchQueue.global().async {
sleep(seconds)
print("\(something) is downloaded")
completionHandler()
group.leave()
}
group.wait()
}
NSOperation
/ NSOperationQueue
设置:
let opA = BlockOperation() {
self.download("A")
}
let opB = BlockOperation() {
self.download("B")
}
let opC = BlockOperation() {
self.download("C")
}
let opD = BlockOperation() {
self.download("D", 4)
}
let opE = BlockOperation() {
self.download("E")
}
opB.addDependency(opA)
opC.addDependency(opA)
opE.addDependency(opB)
opE.addDependency(opC)
opE.addDependency(opD)
let operationQueue = OperationQueue()
operationQueue.addOperations([opA, opB, opC, opD, opE], waitUntilFinished: false)
答案 1 :(得分:0)
您的原始努力似乎与我非常接近。您可以稍作调整:将B
,C
和D
设为完成触发E
的组。
A
可能是另一个小组,但是由于这是一项任务,所以我不明白这一点。完成后触发B
和C
。
请注意,与您的问题和其他答案中的某些示例代码不同,在下面的代码中,D
和A
可以立即启动并并行运行。
let q = DispatchQueue(label: "my-queue", attributes: .concurrent)
let g = DispatchGroup()
func taskA() { print("A") }
func taskB() { print("B"); g.leave() }
func taskC() { print("C"); g.leave() }
func taskD() { print("D"); g.leave() }
func taskE() { print("E") }
g.enter()
g.enter()
g.enter()
q.async {
taskA()
q.async(execute: taskB)
q.async(execute: taskC)
}
q.async(execute: taskD)
g.notify(queue: q, execute: taskE)
答案 2 :(得分:0)
您可以使用此框架实现异步/等待模式-https://github.com/belozierov/SwiftCoroutine
当您调用await时,它不会阻塞线程,而只会挂起协程,因此您也可以在主线程中使用它。
func awaitAPICall(_ url: URL) throws -> String? {
let future = URLSession.shared.dataTaskFuture(for: url)
let data = try future.await().data
return String(data: data, encoding: .utf8)
}
func load(url: URL) {
DispatchQueue.main.startCoroutine {
let result1 = try self.awaitAPICall(url)
let result2 = try self.awaitAPICall2(result1)
let result3 = try self.awaitAPICall3(result2)
print(result3)
}
}
答案 3 :(得分:-1)
我想展示使用Scala之类的替代解决方案:
let result = funcA().flatMap { resultA in
return [funcB(param: resultA.0),
funcC(param: resultA.1),
funcD()]
.fold(initial: [String]()) { (combined, element) in
return combined + [element]
}
}.flatMap { result in
return funcE(param: result)
}.map { result in
print(result)
}
基本上就是这样。它处理错误(隐式)并且是线程安全的。 No Operation子类;)
注意,仅当A成功完成时才会调用funcD
。由于funcA()
可能会失败,因此调用它是没有意义的。但是,如果需要,代码可以很容易地进行调整以使其成为可能。
请将此与我使用Dispatch Groups and Dispatch Queues的其他解决方案中的foo()
功能进行比较。
下面是异步函数定义的示例,每个函数都将结果传递给下一个函数:
func funcA() -> Future<(String, String)> {
print("Start A")
let promise = Promise<(String, String)>()
DispatchQueue.global().asyncAfter(deadline: .now() + 3) {
print("Complete A")
promise.complete(("A1", "A2"))
}
return promise.future
}
func funcB(param: String) -> Future<String> {
print("Start B")
let promise = Promise<String>()
DispatchQueue.global().asyncAfter(deadline: .now() + 1) {
print("Complete B")
promise.complete("\(param) -> B")
}
return promise.future
}
func funcC(param: String) -> Future<String> {
print("Start C")
let promise = Promise<String>()
DispatchQueue.global().asyncAfter(deadline: .now() + 2) {
print("Complete C")
promise.complete("\(param) -> C")
}
return promise.future
}
func funcD() -> Future<String> {
print("Start D")
let promise = Promise<String>()
DispatchQueue.global().asyncAfter(deadline: .now() + 4) {
print("Complete D")
promise.complete("D")
}
return promise.future
}
func funcE(param: [String]) -> Future<String> {
print("Start E")
let promise = Promise<String>()
DispatchQueue.global().asyncAfter(deadline: .now() + 4) {
print("Complete E")
promise.complete("\(param) -> E")
}
return promise.future
}
将其打印到控制台:
Start A
Complete A
Start B
Start C
Start D
Complete B
Complete C
Complete D
Start E
Complete E
["A1 -> B", "A2 -> C", "D"] -> E
提示:有几个Future和Promise库可用。