Swift: управляйте зависимыми функциями, избегайте ада обратного вызова - PullRequest
0 голосов
/ 29 ноября 2018

Как мне лучше разработать следующий код?У меня такое ощущение, что код может привести к обратному вызову ада.Каждая функция зависит от завершения предыдущей.

Текущее решение (плохое):

@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 голосов
/ 29 ноября 2018

Вы можете начать с извлечения замыканий в переменные:

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

Что снова будет намного проще.

0 голосов
/ 29 ноября 2018

Взгляните на будущее и обещания, два связанных шаблона проектирования, которые очень хорошо решают эту проблему.Моя компания использует BrightFutures, стороннюю библиотеку, которая предлагает достойную реализацию обоих.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...