Верижно зависимите сигнали в ReactiveCocoa

В ReactiveCocoa, ако свързваме няколко зависими сигнала, трябва да използваме subscribeNext: за следващия сигнал във веригата, за да получим стойността, произведена от предишния сигнал (например резултат от асинхронна операция). След известно време кодът се превръща в нещо подобно (ненужните подробности са пропуснати):

RACSignal *buttonClickSignal = [self.logIn rac_signalForControlEvents:UIControlEventTouchUpInside];

[buttonClickSignal subscribeNext:^(UIButton *sender) {    // signal from a button click
    // prepare data

    RACSignal *loginSignal = [self logInWithUsername:username password:password];    // signal from the async network operation

    [loginSignal subscribeNext:^void (NSDictionary *json) {
        // do stuff with data received from the first network interaction, prepare some new data

        RACSignal *playlistFetchSignal = [self fetchPlaylistForToken:token];         // another signal from the async network operation

        [playlistFetchSignal subscribeNext:^(NSDictionary *json) {
            // do more stuff with the returned data
        }];

        // etc
    }];
}];

Това непрекъснато нарастващо влагане не изглежда много по-добре от нереактивния пример, който е даден в документацията:

[client logInWithSuccess:^{
    [client loadCachedMessagesWithSuccess:^(NSArray *messages) {
        [client fetchMessagesAfterMessage:messages.lastObject success:^(NSArray *nextMessages) {
            NSLog(@"Fetched all messages.");
        } failure:^(NSError *error) {
            [self presentError:error];
        }];
    } failure:^(NSError *error) {
        [self presentError:error];
    }];
} failure:^(NSError *error) {
    [self presentError:error];
}];

Изпускам ли нещо? Има ли по-добър модел на верижно зависима работа в ReactiveCocoa?


person Sergey Mikhanov    schedule 03.04.2013    source източник


Отговори (1)


Това е, когато RACStream и RACSignal операторите наистина започват да стават полезни. Във вашия конкретен пример можете да използвате -flattenMap:, за да включите резултатите в нови сигнали:

[[[buttonClickSignal
    flattenMap:^(UIButton *sender) {
        // prepare 'username' and 'password'
        return [self logInWithUsername:username password:password];
    }]
    flattenMap:^(NSDictionary *json) {
        // prepare 'token'
        return [self fetchPlaylistForToken:token];
    }]
    subscribeNext:^(NSDictionary *json) {
        // do stuff with the returned playlist data
    }];

Ако не се нуждаете от резултатите от нито една стъпка, можете да използвате -sequenceMany: или -sequenceNext: вместо това за подобен ефект (но за по-ясно изразяване на намерението).

person Justin Spahr-Summers    schedule 05.04.2013
comment
Това решение работи за мен. Написах статия в блог, която обяснява този подход по-подробно. - person Richard H Fung; 05.08.2013
comment
@Justin, имаш ли нещо против да разясниш повече за правилния начин за обработка на грешки по пътя? - person DogpatchTech; 01.03.2014
comment
@DogpatchTech Можете да използвате -catch: или -catchTo: за това. Вижте това обяснение. - person Justin Spahr-Summers; 03.03.2014
comment
-sequenceMany: и -sequenceNext: са отхвърлени във 2.0+; -sequenceMany: трябва да се замени с -flattenMap:, като се игнорират блоковите аргументи; -sequenceNext: се заменя с -then:. (Вижте записа в регистъра на промените тук: github.com /ReactiveCocoa/ReactiveCocoa/blob/master/) - person febeling; 15.05.2014
comment
@JustinSpahr-Summers как бихте пренаписали горния код, ако fetchPlaylistForToken трябваше да бъде извикан след loginWithUsername completed вместо да изпрати събитието next? - person Valerio Santinelli; 12.03.2015
comment
@ValerioSantinelli Оператор като -then:, или -materialize, или нещо подобно. Това обаче звучи като миризма на код. - person Justin Spahr-Summers; 12.03.2015
comment
@JustinSpahr-Summers можеш ли да използваш concat? - person onmyway133; 31.03.2015