Swift:控制依赖函数,避免回调地狱

时间:2018-11-29 17:51:39

标签: swift

如何更好地设计以下代码?我感觉代码会导致回调地狱。每个功能都取决于前一个功能的完成。

当前解决方案(错误):

@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()
    }

}

但是我该怎么做呢?那是个好方法吗?

2 个答案:

答案 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()
        }
    }
}

这将再简单得多。