flatMap 不返回 onCompleted

Posted

技术标签:

【中文标题】flatMap 不返回 onCompleted【英文标题】:flatMap Not returning onCompleted 【发布时间】:2017-02-16 13:23:55 【问题描述】:

我创建了下面的函数,链接了多个可观察对象,但是无论我做什么,它似乎都没有调用 completed 吗?它只返回以下内容:

(facebookSignInAndFetchData()) -> subscribed
(facebookSignInAndFetchData()) -> Event next(())

即使当我 debug 单独观察时,它们都返回 completed

这是我的链接函数

func facebookSignInAndFetchData() 


    observerFacebook.flatMap  (provider: FacebookProvider) in
        return provider.login()
        .flatMap  token in
            return self.loginViewModel.rx_authenticate(token: token)
        .flatMap 
            return self.loginViewModel.fetchProfileData()
        .debug().subscribe(onError:  error in

            //Guard unknown ErrorType
            guard let err = error as? AuthError else 
                //Unknown error message
                self.alertHelper.presentAlert(L10n.unknown)
                return
            

            //error message handling
            switch err 
            case .notLoggedIn:
                print("not logged in")
                break
            default:
                self.alertHelper.presentAlert(err.description)
            

        , onCompleted: 
            self.goToInitialController()
        ).addDisposableTo(self.disposeBag)


rx_authenticate

func rx_authenticate(token: String) -> Observable<Void> 


    return Observable.create( observer in
        let credentials = SyncCredentials.facebook(token: token)
        SyncUser.logIn(with: credentials, server: URL(string: Globals.serverURL)!, onCompletion:  user, error in

            //Error while authenticating
            guard error == nil else 
                print("error while authenticating: \(error!)")
                observer.onError(AuthError.unknown)
                return
            

            //Error while parsing user
            guard let responseUser = user else 
                print("error while authenticating: \(error!)")
                observer.onError(AuthError.unknown)
                return
            

            //Authenticated
            setDefaultRealmConfiguration(with: responseUser)

            //next
            observer.onNext()

            //completed
            observer.onCompleted()


        )

        return Disposables.create()
    )

fetchProfileData

func fetchProfileData() -> Observable<Void> 

     return Observable.create( observer in

        //Fetch facebookData
        let params = ["fields" : "name, picture.width(480)"]
        let graphRequest = GraphRequest(graphpath: "me", parameters: params)
        graphRequest.start 
            (urlResponse, requestResult) in
            switch requestResult 
            case .failed(_):
                //Network error
                observer.onError(AuthError.noConnection)
                break
            case .success(let graphResponse):

                if let responseDictionary = graphResponse.dictionaryValue 

                    guard let identity = SyncUser.current?.identity else 
                        //User not logged in
                        observer.onError(AuthError.noUserIdentity)
                        return
                    

                    //Name
                    let name = responseDictionary["name"] as! String

                    //Image dictionary
                    let pictureDic = responseDictionary["picture"] as! [String: Any]
                    let dataDic = pictureDic["data"] as! [String: Any]
                    let imageHeight = dataDic["height"] as! Int
                    let imageWidth = dataDic["width"] as! Int
                    let url = dataDic["url"] as! String

                    //Create Person object
                    let loggedUser = Person()
                    loggedUser.id = identity
                    loggedUser.name = name

                    //Create photo object
                    let photo = Photo()
                    photo.height = imageHeight
                    photo.width = imageWidth
                    photo.url = url

                    //Append photo object to person object
                    loggedUser.profileImage = photo

                    //Save userData
                    let realm = try! Realm()
                    try! realm.write 
                        realm.add(loggedUser, update: true)
                    

                    //next
                    observer.onNext()

                    //completed
                    observer.onCompleted()

                 else 
                    //Could not retrieve responseData
                    observer.onError(AuthError.noResponse)
                
            
        



        return Disposables.create()
    )



观察者Facebook

//FacebookProvider
private lazy var observerFacebook: Observable<FacebookProvider>! = 
    self.facebookButton.rx.tap.map 

        return FacebookProvider(parentController: self)
    
()

【问题讨论】:

看起来不错。可以添加 rx_authenticate 和 fetchProfileData 实现吗? 现在已添加。提前谢谢你 你还能为observerFacebook添加定义吗? 你也需要登录功能吗? observerFacebook 已添加 这两个函数看起来都不错 - 应该总是以完成或错误完成。你能检查一下这是否正确——observer.onCompleted 都被调用了吗? 【参考方案1】:

该链从调用observerFacebook 开始,它返回一个可观察对象,每次点击facebookButton 时都会发出值。

这个 observable 只有在 facebookButton 被释放时才会完成,很可能是当持有它的视图控制器从屏幕上移除时。

链的其余部分将mapflatMap,但永远不会强制完成,因为另一个点击会再次触发整个链。

解决此问题的简单方法是在facebookButton.rx.tap 上添加对take(1) 的调用,以便函数定义如下:

private lazy var observerFacebook: Observable<FacebookProvider>! = 
    self.facebookButton.rx.tap
    .take(1)
    .map 
        return FacebookProvider(parentController: self)
    
()

现在,observerFacebook 在第一次点击后完成,您应该会看到对 onCompleted 的调用。

请注意,如果您想在另一个点击时再次执行它,则需要重新订阅错误链。

【讨论】:

如果其他人来到这里寻找 rxjava 的答案,我使用的成语是 Observable.create(s -&gt; s.onNext(new Object()); emitter = s; ).flatMap($ -&gt; realObservable),然后 emitter.onComplete() 会将 onComplete() 传播给订阅者。

以上是关于flatMap 不返回 onCompleted的主要内容,如果未能解决你的问题,请参考以下文章

特征的 flatMap writeValue 不返回 Observable

Java8的flatMap如何处理有异常的函数

spark 的一些常用函数 filter,map,flatMap,lookup ,reduce,groupByKey

Java8中map()和flatMap()的区别

使用 if/else 语句返回 observable 不适用于 RxSwift flatMap

理解Swift中map 和 flatMap对集合的作用