37

我正在使用AFNetworking对 Web 服务进行异步调用。其中一些调用必须链接在一起,其中调用 A 的结果由调用 B 使用,调用 B 使用调用 C 等。

AFNetworking 处理异步调用的结果,并在创建操作时设置成功/失败块:

NSURL *url = [NSURL URLWithString:@"http://api.twitter.com/1/statuses/public_timeline.json"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
    NSLog(@"Public Timeline: %@", JSON);
} failure:nil];
[operation start];

这会导致嵌套的异步调用块很快变得不可读。当任务不相互依赖而必须并行执行并且执行取决于所有操作的结果时,情况会更加复杂。

似乎更好的方法是利用Promise框架来清理控制流。

我遇到了MAFuture,但不知道如何最好地将它与 AFNetworking 集成。由于异步调用可能有多个结果(成功/失败)并且没有返回值,因此它似乎不是一个理想的选择。

任何指针或想法将不胜感激。

4

6 回答 6

19

我为此创建了一个轻量级的解决方案。它被称为 Sequencer,它在github 上

它使链接 API 调用(或任何其他异步代码)变得简单明了。

下面是一个使用 AFNetworking 的例子:

Sequencer *sequencer = [[Sequencer alloc] init];

[sequencer enqueueStep:^(id result, SequencerCompletion completion) {
    NSURL *url = [NSURL URLWithString:@"https://alpha-api.app.net/stream/0/posts/stream/global"];
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
        completion(JSON);
    } failure:nil];
    [operation start];
}];

[sequencer enqueueStep:^(NSDictionary *feed, SequencerCompletion completion) {
    NSArray *data = [feed objectForKey:@"data"];
    NSDictionary *lastFeedItem = [data lastObject];
    NSString *cononicalURL = [lastFeedItem objectForKey:@"canonical_url"];

    NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:cononicalURL]];
    AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
    [operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
        completion(responseObject);
    } failure:nil];
    [operation start];
}];

[sequencer enqueueStep:^(NSData *htmlData, SequencerCompletion completion) {
    NSString *html = [[NSString alloc] initWithData:htmlData encoding:NSUTF8StringEncoding];
    NSLog(@"HTML Page: %@", html);
    completion(nil);
}];

[sequencer run];
于 2012-12-11T20:57:05.683 回答
10

我还没有使用它,但听起来Reactive Cocoa的设计目的就是按照您的描述。

于 2012-06-08T18:06:05.840 回答
10

在 Gowalla 中使用 AFNetworking 在成功块中将调用链接在一起的情况并不少见。

我的建议是尽可能将网络请求和序列化因素考虑到模型中的类方法中。然后,对于需要进行子请求的请求,您可以在成功块中调用这些方法。

此外,如果您还没有使用它,可以AFHTTPClient大大简化这些复杂的网络交互。

于 2012-06-11T15:05:45.320 回答
6

PromiseKit可能很有用。它似乎是更流行的 Promise 实现之一,其他人编写了类别以将其与 AFNetworking 等库集成,请参阅PromiseKit-AFNetworking

于 2014-07-29T18:52:35.317 回答
4

在 Github 上有一个 CommonJS 风格的 Promise 的 Objective-C 实现:

https://github.com/mproberts/objc-promise

示例(取自 Readme.md)

Deferred *russell = [Deferred deferred];
Promise *promise = [russell promise];

[promise then:^(NSString *hairType){
    NSLog(@"The present King of France is %@!", hairType);
}];

[russell resolve:@"bald"];

// The present King of France is bald!

我还没有尝试过这个库,但尽管这个例子有点让人印象深刻,但它看起来“很有希望”。(对不起,我无法抗拒)。

于 2013-03-21T11:03:40.130 回答
0

您可以结合NSBlockOperation使用semaphore来实现它:

- (void)loadDataByOrderSuccess:(void (^)(void))success failure:(void (^)(void))failure {
    // first,load data1
    NSBlockOperation * operation1 = [NSBlockOperation blockOperationWithBlock:^{
        dispatch_semaphore_t sema = dispatch_semaphore_create(0);
        [self loadData1Success:^{
            dispatch_semaphore_signal(sema);
        } failure:^{
            !failure ?: failure();
        }];
        dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
    }];
    // then,load data2
    NSBlockOperation * operation2 = [NSBlockOperation blockOperationWithBlock:^{
        dispatch_semaphore_t sema = dispatch_semaphore_create(0);
        [self loadData2Success:^{
            dispatch_semaphore_signal(sema);
        } failure:^{
            !failure ?: failure();
        }];
        dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
    }];
    // finally,load data3
    NSBlockOperation * operation3 = [NSBlockOperation blockOperationWithBlock:^{
        dispatch_semaphore_t sema = dispatch_semaphore_create(0);
        [self loadData3Success:^{
            dispatch_semaphore_signal(sema);
        } failure:^{
            !failure ?: failure();
        }];
        dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
        !success ?: success();
    }];
    [operation2 addDependency:operation1];
    [operation3 addDependency:operation2];
    NSOperationQueue * queue = [[NSOperationQueue alloc] init];
    [queue addOperations:@[operation1, operation2, operation3] waitUntilFinished:NO];
}
于 2018-07-22T11:44:20.427 回答