如何按顺序执行操作并更新 UI

Posted

技术标签:

【中文标题】如何按顺序执行操作并更新 UI【英文标题】:How can execute operations sequentially and update UI 【发布时间】:2021-11-11 15:53:58 【问题描述】:

我正在尝试按顺序执行一堆操作,并在每次操作开始和完成时更新我的​​ UI(我必须更新操作状态图标颜色)。

在我的(工作)代码下方:

class SyncManager 

private let disposeBag = DisposeBag()

// MARK: - Private Init

private init()  

// MARK: - Public Constants

static let shared = SyncManager()

let refreshTokenStatus = BehaviorRelay<SyncStatus>(value: .todo)
let updateCatalogDataStatus = BehaviorRelay<SyncStatus>(value: .todo)
let insertDataStatus = BehaviorRelay<SyncStatus>(value: .todo)
let getDataStatus = BehaviorRelay<SyncStatus>(value: .todo)

func startDatabaseSync(completion: @escaping ((Result<Void, Error>) -> Void)) 
    refreshTokenStatus.accept(.todo)
    updateCatalogDataStatus.accept(.todo)
    insertDataStatus.accept(.todo)
    getDataStatus.accept(.todo)
    RefreshTokenManager.shared.refreshToken().do(onSuccess:  [self]_ in
        print("RefreshTokenManager onSuccess")
        refreshTokenStatus.accept(.completed)
    , onError:  [self] error in
        print("RefreshTokenManager onError: \(error)")
        refreshTokenStatus.accept(.error)
    , onSubscribe:  [self] in
        print("RefreshTokenManager onSubscribe")
        refreshTokenStatus.accept(.running)
    ).asObservable().concatMap  result in
        UpdateCatalogDataSyncManager.shared.updateCatalogData().do(onSuccess:  [self] in
            print("UpdateCatalogDataSyncManager onSuccess")
            updateCatalogDataStatus.accept(.completed)
        , onError:  [self] error in
            print("UpdateCatalogDataSyncManager onError: \(error)")
            updateCatalogDataStatus.accept(.error)
        , onSubscribe:  [self] in
            print("UpdateCatalogDataSyncManager onSubscribe")
            updateCatalogDataStatus.accept(.running)
        ).asObservable().concatMap  result in
            GetDataSyncManager.shared.getData().do  [self] in
                print("GetDataSyncManager onSuccess")
                getDataStatus.accept(.completed)
             onError:  [self] error in
                print("GetDataSyncManager onError: \(error)")
                getDataStatus.accept(.error)
             onSubscribe:  [self] in
                print("GetDataSyncManager onSubscribe")
                getDataStatus.accept(.running)
             onDispose: 
                print("GetDataSyncManager onDispose")
            .asObservable().concatMap  _ in
                InsertDataWorkSyncManager.shared.insertData().do  [self] in
                    print("InsertDataWorkSyncManager onSuccess")
                    insertDataStatus.accept(.completed)
                  onError:  [self] error in
                    print("InsertDataWorkSyncManager onError: \(error)")
                    insertDataStatus.accept(.error)
                 onSubscribe:  [self] in
                    print("InsertDataWorkSyncManager onSubscribe")
                    insertDataStatus.accept(.running)
                 onDispose: 
                    print("InsertDataWorkSyncManager onDispose")
                

            
        
    .subscribe  _ in
        print("SyncManager onNext")
     onError:  error in
        print("SyncManager onError: \(error)")
        completion(.failure(error))
     onCompleted: 
        print("SyncManager onCompleted")
        completion(.success(()))
     onDisposed: 
        print("SyncManager onDisposed")
    .disposed(by: disposeBag)



enum SyncStatus 
    
    case todo
    case completed
    case error
    case running
    case partial
    

我的ViewController

SyncManager.shared.refreshTokenStatus.skip(1).subscribe(onNext:  status in
        // Update UI
    ).disposed(by: disposeBag)
    SyncManager.shared.updateCatalogDataStatus.skip(1).subscribe(onNext:  status in
        // Update UI
    ).disposed(by: disposeBag)
    SyncManager.shared.insertDataStatus.skip(1).subscribe(onNext:  status in
        // Update UI
    ).disposed(by: disposeBag)

我是RxSwift 的新手(我只使用了一周),所以我想知道是否有更好的方法来实现我的上述目标。

【问题讨论】:

【参考方案1】:

这是一个我认为可行的想法。从概念上讲,这是非常必要的,这使得很难转录为 FRP 的功能声明范式。我保留了相同的外部接口,因此它可以作为替代品。

class SyncManager 
    private init()  
    static let shared = SyncManager()
    
    let refreshTokenStatus = BehaviorRelay<SyncStatus>(value: .todo)
    let updateCatalogDataStatus = BehaviorRelay<SyncStatus>(value: .todo)
    let insertDataStatus = BehaviorRelay<SyncStatus>(value: .todo)
    let getDataStatus = BehaviorRelay<SyncStatus>(value: .todo)
    
    private let disposeBag = DisposeBag()
    
    func startDatabaseSync(completion: @escaping (Result<Void, Error>) -> Void) 
        let sync = Sync.startDatabaseSync()
        disposeBag.insert(
            sync.refreshTokenStatus.bind(to: refreshTokenStatus),
            sync.updateCatalogDataStatus.bind(to: updateCatalogDataStatus),
            sync.insertDataStatus.bind(to: insertDataStatus),
            sync.getDataStatus.bind(to: getDataStatus),
            sync.getDataStatus.subscribe(
                onError:  error in
                    completion(.failure(error))
                ,
                onCompleted: 
                    completion(.success(()))
                
            )
        )
    


struct Sync 
    let refreshTokenStatus: Observable<SyncStatus>
    let updateCatalogDataStatus: Observable<SyncStatus>
    let getDataStatus: Observable<SyncStatus>
    let insertDataStatus: Observable<SyncStatus>
    
    static func startDatabaseSync() -> Sync 
        let refreshTokenStatus = handle(RefreshTokenManager.shared.refreshToken(), after: .just(.completed))
            .catchAndReturn(.error)
        let updateCatalogDataStatus = handle(UpdateCatalogDataSyncManager.shared.updateCatalogData(), after: refreshTokenStatus)
            .catchAndReturn(.error)
        let getDataStatus = handle(GetDataSyncManager.shared.getData(), after: updateCatalogDataStatus)
            .catchAndReturn(.error)
        let insertDataStatus = handle(InsertDataWorkSyncManager.shared.insertData(), after: getDataStatus)
            .catchAndReturn(.error)
        
        return Sync(
            refreshTokenStatus: refreshTokenStatus,
            updateCatalogDataStatus: updateCatalogDataStatus,
            getDataStatus: getDataStatus,
            insertDataStatus: insertDataStatus
        )
    


func handle(_ operation: Single<Void>, after: Observable<SyncStatus>) -> Observable<SyncStatus> 
    after
        .ignoreElements()
        .asCompletable()
        .andThen(
            operation
                .map  SyncStatus.completed 
                .asObservable()
                .startWith(SyncStatus.running)
        )
        .startWith(.todo)


enum SyncStatus 
    case todo
    case completed
    case error
    case running

如果您要重新安排其余的代码,使其更符合 Rx 风格,那么您可能会变得更简洁...

【讨论】:

谢谢丹尼尔!我想我有很多研究:)disposed(by:disposeBag)失踪了吗? 没有 disposeBag.insert() 替换它。 好的!为什么你说我的代码在概念上是命令式的?什么意思? “函数式反应式编程的本质是在声明时完全指定一个值的动态行为。” -- Heinrich Apfelmus BehaviorRelays,就其性质而言,不指定声明时的动态行为。加入我们的RxSwift slack 进行更深入的讨论。

以上是关于如何按顺序执行操作并更新 UI的主要内容,如果未能解决你的问题,请参考以下文章

Jmeter-按顺序执行请求

如何按特定顺序从 select 中执行 Oracle SQL 更新?

如何按顺序执行承诺并返回所有结果[重复]

Xcode 7 UI 测试顺序

reactjs/flux:按顺序执行动作(ajax)

在 Python 中按顺序执行命令?