до сих пор не могу понять, как заказать асинхронные задачи для удаления пользователя

Поэтому я пытаюсь обеспечить выполнение набора асинхронных задач в определенном порядке при удалении пользователя.

Итак, что я хочу:

  • Проверьте, добавил ли пользователь гостей к своей покупке
  • если у пользователя нет гостей или каких-либо покупок, вернитесь из функции и продолжите процесс удаления (пункт 6)
  • если у пользователя есть гости для каких-либо покупок, удалите каждого гостя
  • как только все гости будут удалены, продолжайте и удаляйте все сделанные ими покупки
  • как только все сделанные покупки будут удалены, удалите самого пользователя из Firestore.
  • Через 2 секунды после этого я удаляю пользователя из аутентификации firebase, чтобы убедиться, что нет сбоев при попытке удалить документы с пустым пользователем
  • затем я просто перехожу в главное меню

Итак, я пытаюсь добиться этого с помощью этого блока кода в моей функции:

      let deleteAction = UIAlertAction(title: "Delete", style: .destructive) { (deletion) in
            let semaphore = DispatchSemaphore(value: 0)
            
            
            self.deleteButton.isHidden = true
            self.loadingToDelete.alpha = 1
            self.loadingToDelete.startAnimating()
            
            DispatchQueue.global(qos: .background).async {
                self.db.collection("student_users/\(user.uid)/events_bought").getDocuments { (querySnapshot, error) in
                    guard error == nil else {
                        print("The docs couldn't be retrieved for deletion.")
                        return
                    }
                    
                    guard querySnapshot?.isEmpty == false else {
                        print("The user being deleted has no events purchased.")
                        return
                    }
                    
                    for document in querySnapshot!.documents {
                        let docID = document.documentID
                        
                        self.db.collection("student_users/\(user.uid)/events_bought/\(docID)/guests").getDocuments { (querySnap, error) in
                            guard querySnap?.isEmpty == false else {
                                print("The user being deleted has no guests with his purchases.")
                                return
                            }
                            
                            for doc in querySnap!.documents {
                                let guest = doc.documentID
                                self.db.document("student_users/\(user.uid)/events_bought/\(docID)/guests/\(guest)").delete { (error) in
                                    guard error == nil else {
                                        print("Error deleting guests while deleting user.")
                                        return
                                    }
                                    print("Guests deleted while deleting user!")
                                    semaphore.signal()
                                }
                                semaphore.wait()
                            }
                        }
                    }
                }
                
                
                self.db.collection("student_users/\(user.uid)/events_bought").getDocuments { (querySnapshot, error) in
                    guard error == nil else {
                        print("There was an error retrieving docs for user deletion.")
                        return
                    }
                    guard querySnapshot?.isEmpty == false else {
                        return
                    }
                    for document in querySnapshot!.documents {
                        let docID = document.documentID
                        
                        self.db.document("student_users/\(user.uid)/events_bought/\(docID)").delete { (err) in
                            guard err == nil else {
                                print("There was an error deleting the the purchased events for the user being deleted.")
                                return
                            }
                            print("Purchases have been deleted for deleted user!")
                            semaphore.signal()
                        }
                        semaphore.wait()
                    }
                    
                }
        
                
                self.db.document("student_users/\(user.uid)").delete(completion: { (error) in
                    
                    guard error == nil else {
                        print("There was an error deleting the user document.")
                        return
                    }
                    print("User doc deleted!")
                    semaphore.signal()
                })
                semaphore.wait()
                
                
            }
            
            DispatchQueue.main.asyncAfter(deadline: .now()+1.5) {
                user.delete(completion: { (error) in
                    guard error == nil else {
                        print("There was an error deleting user from the system.")
                        return
                    }
                    print("User Deleted.")
                })
               
                
            }
        
            self.loadingToDelete.stopAnimating()
            self.performSegue(withIdentifier: Constants.Segues.studentUserDeletedAccount, sender: self)
            
            
        }

Последние пару часов я пытался поиграться с ним и внедрить его в свой код, но он просто не делает того, что я ожидаю. Я бы прочитал статьи и примеры DispatchSemaphore() онлайн, но сценарии не совсем такие же, как у меня, в том, что касается того, что я конкретно хочу делать.

Когда это предупреждающее действие запускается при нажатии, ничего не печатается, и оно просто заканчивается вечно, пользователь не удаляется из Firebase Auth, и в базе данных Firestore все еще есть оставшиеся данные, например:

Я просто в основном хочу выяснить, как лучше всего упорядочить эти асинхронные задачи в упорядоченном списке выше и получить чистое удаление пользователя без остатка в базе данных. Заранее спасибо.

1 ответ

Вы должны справиться с этим с помощью облачной функции Firebase, которая имеет несколько способов реагирования на запросы клиентов и изменения базы данных. Это требует выставления счетов и переноса вашего кода на javascript с помощью узла v10, что довольно просто из-за согласованных методов firebase на большинстве языков.

Функция Firebase

Два популярных метода - это просто импорт модуля облачных функций firebase в ваше приложение или вызов запроса через https, каждый из которых имеет свои собственные точки входа с плюсами и минусами, которые стоит прочитать.

Оттуда вы должны удалить основные файлы, которые немедленно повлияют на пользователя, а затем обновить клиент по их результатам, прежде чем приступить к очистке остаточных файлов.

Триггер Firestore

Альтернативой, которая столь же надежна и более управляема от потенциальных злоупотреблений, является запуск триггера на основе удаления документа, вы можете использовать его, чтобы затем инициировать удаление и очистку других документов.

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

https://firebase.google.com/docs/functions/firestore-events#function_triggers

Обновление: асинхронный

Асинхронные методы - это просто функции, помеченные как asyncкоторые позволяют задачам работать без блокирующей структуры, это позволяет запускать несколько задач независимо от другой. Однако, чтобы приостановить код и дождаться выполнения каких-либо действий, вы можете добавить флаг

      function resolveAfter2Seconds() {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve('resolved');
    }, 2000);
  });
}

async function asyncCall() {
  console.log('calling');
  const result = await resolveAfter2Seconds();
  console.log(result);
  // expected output: "resolved"
}

asyncCall();

ссылка: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function

Обновление: обещания

Обещания работают так же, как асинхронные функции, и выполняются независимо от родительской функции, и сами могут быть помечены await если нужно.

      const myPromise = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('foo');
  }, 300);
});

myPromise
  .then(handleResolvedA, handleRejectedA)
  .then(handleResolvedB, handleRejectedB)
  .then(handleResolvedC, handleRejectedC);

ссылка: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise