如何更好地设计以下代码?我感觉代码会导致回调地狱。每个功能都取决于前一个功能的完成。
当前解决方案(错误):
@objc func restoreDocuments(UID: UID) {
DispatchQueue.global(qos: .background).async {
//1. Load user details from RemoteServer#1
UserManager.RemoteServer.loadUserFromRemoteServer(userUID: UID) { (userDict) in
//2. After user is loaded save user to local database
UserManager.LocalDB.saveUser(userData: userDict, completion: {
//After User is restored, restore his documents from RemoteServer#2 (IDs provided in userDetails)
let userDocumentsArray = getDocumentIDsFromUser(userUID: UID)
//Loop through array to get every ID
for ID in userDocumentsArray{
//load each document by ID
loadDocumentsRemote(documentID: ID) { (document) in
//Save loaded document
saveDocumentsLocal(document, completion: {
//At the end populate the UI with the restored documents
DispatchQueue.main.async {
populateUI()
}
})
})
}
})
}
}
我会想像下面的代码。但是我不知道如何相互交流不同的步骤。因此,任务2在任务1完成之前不会开始。
我的想象(简化):
@objc func restoreDocuments(UID: UID) {
//1. Restore User
UserManager.RemoteServer.loadUser(UID){ (user) in }
UserManager.LocalDB.saveUser(user)
// -> WHEN FINISH PROCCED TO STEP 2
//2. Load Documents
UserManager.LocalDB.getDocumentIDsFromUser( { (IdArray) in
for ID in IdArray {
RemoteServer.DocManager.loadDocument(ID) { (retrievedDocument) in
LocalDB.DocManager.saveDocument(retrievedDocument)
}
}
}
// -> WHEN FINISH PROCCED TO STEP 3
//3. Finish
DispatchQueue.main.async {
populateUI()
}
}
但是我该怎么做呢?那是个好方法吗?
答案 0 :(得分:1)
看看期货和承诺,这两个相关的设计模式很好地解决了这个问题。我的公司使用BrightFutures,这是一个第三方库,可以很好地实现两者。
答案 1 :(得分:0)
您可以先将闭包提取为变量:
let onDocumentsSaved: () -> Void = {
DispatchQueue.main.async {
populateUI()
}
}
let onDocumentsLoaded: (Document) -> Void { document in
saveDocumentsLocal(document, completion: onDocumentsSaved)
}
// continue with other closures, in reverse order of execution
这将清除您的缩进,并且步骤将清晰可见。如果您要一步一步等待多个步骤(例如,多个文档),则可以使用DispatchGroup
。
每个步骤也可以轻松地提取到函数中,或者,您可以使您的类作为状态机工作。
此外,将连接的方法分组为实用方法也是一个好主意,例如您的 load 和 save 可以使用完成处理程序分组为一个方法,例如:
func loadUserAndSave(userId, completion)
func loadDocumentsAndSave(userId, completion)
然后您的方法可以简化为(伪代码):
loadUserAndSave(userId) {
loadDocumentsAndSave {
DispatchQueue.main.async {
populateUI()
}
}
}
这将再简单得多。