等待异步调用在完成块内完成 [英] Waiting for an asyncronous call to finish within a completion block

查看:134
本文介绍了等待异步调用在完成块内完成的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我目前正在开发一个POC应用程序,我之前在此发布了此处。我试图处理身份验证令牌的自动刷新,如果我的服务器给我401错误(未经授权)。

I am currently working on a POC app, I have previously posted about it here. I am trying to handle automatic refreshing of an authentication token should my server give me a 401 error (unauthorised).

这是我的演示函数,它从服务器请求一些信息(我可以故意发送它有效/无效的授权令牌)

Here is my demo function that requests some information from the server (I can deliberately send it valid/invalid auth tokens)

NSInteger retryAttempts = 0;
NSInteger retryMax = 1;

- (void) requestDataForUser {
    NSLog(@"requestDataForUser - Called");

    //Indicate Network Activity
    dispatch_async(dispatch_get_main_queue(), ^{
        [UIApplication sharedApplication].networkActivityIndicatorVisible = TRUE;
    });

    //Build request URL String
    NSString *requestString = [NSString stringWithFormat:@"%@%@%@",baseURL,requestURL,@"3"];//Change to allow change in username here.

    //Get auth token
    NSString *accessToken = [SAMKeychain passwordForService:kServer account:kKeyAccessToken];
    NSString *requestAuthorization = [NSString stringWithFormat:@"%@ %@", @"Bearer", accessToken];

    //Initialize url request
    NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];

    //Set the url for the request
    [request setURL:[NSURL URLWithString:requestString]];

    //Set HTTP method for request
    [request setHTTPMethod:@"GET"];

    //Set HTTP header field with the authorization token
    [request setValue:requestAuthorization forHTTPHeaderField:@"Authorization"];

    //Create full request
    NSURLSession *session = [NSURLSession sharedSession];

    __weak typeof (self) weakSelf = self;
    NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request
                                                completionHandler:^(NSData *data, NSURLResponse *response, NSError *error){

                                    NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response;
                                                    NSLog(@"Status Code: %ld\n",(long)httpResponse.statusCode);

                                                    NSString *message = [NSHTTPURLResponse localizedStringForStatusCode:httpResponse.statusCode];
                                                    NSLog(@"Message: %@", message);

                                                    NSLog(@"requestDataForUser - Responce from server");

                                                    //Check for an error, if there is no error we proceed.
                                                    if (!error) {

                                                        if (retryAttempts <= retryMax) {
                                                        switch (httpResponse.statusCode) {
                                                            case 200 ... 299:
                                                                NSLog(@"SUCCESS");
                                                                NSLog(@"Performing any completion related functions!");
                                                                break;
                                                            case 401:
                                                                NSLog(@"401 Challenge - Retrying Authentication, Attempt %ld", (long)retryAttempts);

                                                                [weakSelf refreshAuth];
                                                                [weakSelf requestDataForUser];//retries this function

                                                                retryAttempts += 1;
                                                                break;

                                                        }}
                                                        else {
                                                            NSLog(@"401 Error Recieved - Retried credentials %ld time(s), please check your details are correct", (long)retryMax);
                                                            retryAttempts = 0; //Reset retry counter
                                                            //Alert controller?
                                                        }
                                                        //Get que and perform any UI changes
                                                        dispatch_async(dispatch_get_main_queue(), ^{

                                                            [UIApplication sharedApplication].networkActivityIndicatorVisible = FALSE;
                                                        });
                                                    }
                                                    else {
                                                        //Failed request
                                                        NSLog(@"requestDataForUser - error : %@", error.description);
                                                        dispatch_async(dispatch_get_main_queue(), ^{
                                                            [UIApplication sharedApplication].networkActivityIndicatorVisible = FALSE;
                                                        });
                                                    }
                                                }];
    [dataTask resume];

}

我遇到的问题来自401挑战请求部分。我想要做的是请求/刷新一个新令牌(在最后一次迭代中刷新,但目前我的服务器在令牌刷新时有点击中/未命中,因此我在此示例中请求新令牌)。让我们来看看我的服务器挑战部分:

The problems I am having with this come in the 401 challenge section of the request. What I want to do is request/refresh a new token (refresh in the final iteration but currently my server is a bit hit/miss on token refreshes so I am requesting a new token in this example). So lets look at my server challenge section:

case 401:
         NSLog(@"401 Challenge - Retrying Authentication, Attempt %ld", (long)retryAttempts);
         [weakSelf refreshAuth];
         [weakSelf requestDataForUser];//retries this function
         retryAttempts += 1;
         break;

所以我在这里打印出尝试号码,我可以手动设置这个'重试块'直到它放弃并向用户抛出错误。接下来它会调用一个auth令牌,重试请求并将retryAttempts增加1。

So i am printing out the attempt number here, I can manually set the amount of times that this 'block' is retried until it gives up and throws an error at the user. Next it will call for an auth token, retry the request and increase retryAttempts by 1.

我的问题是,当我请求一个新令牌时,我正在异步执行请求被发送,然后我的函数重试自己(显然没有新的令牌),然后它抛出错误。然后我的令牌返回并打印到控制台,新令牌成功返回。

My problem is that when I request a new token I'm doing it asynchronously so the request is sent off and then my function retries itself (obviously without a new token) and then it throws the error. And then my token returns and prints to the console that a new token returned successfully.

我看过信号量,但我似乎无法让它们工作(因为我的requestAuthToken方法没有完成块)。反正我是否可以强制auth请求同步?

I have had a look at semaphores but I can't seem to get them to work (as my requestAuthToken method has no completion block). Is there anyway I can force the auth request to be syncronous?

我还试图让我的requestAuth方法返回BOOL并在401块中循环bool直到它变为真,但它永远不会被设置为真,而while循环会永远继续。

I have also tried to get my requestAuth method to return a BOOL and loop the bool within the 401 block until it becomes true, however it never gets set to true and the while loop goes on forever.

感谢任何和所有帮助!

推荐答案

假设您可以更改requestAuth的实现,请向requestAuth函数添加完成处理程序参数。

在requestAuth实现中,在收到令牌后调用该处理程序。然后在requestDataForUser中:

Assuming you can change implementation of requestAuth, add a completion handler parameter to requestAuth function.
Inside requestAuth implementation, call that handler after token is received. Then in requestDataForUser:

case 401:
     NSLog(@"401 Challenge - Retrying Authentication, Attempt %ld", (long)retryAttempts);
     [weakSelf refreshAuth withCompletionHandler:weakself.requestDataForUser];
     retryAttempts += 1;
     break;

否则,使用NSOperationQueue并将最大并发操作设置为1:

Otherwise, use NSOperationQueue and set maximum concurrent operation to 1:

NSOperationQueue *queue = [[NSOperationQueue alloc] init];
queue.maxConcurrentOperationCount = 1;

[queue addOperationWithBlock:^{
    [weakself requestAuth];
}];

[queue addOperationWithBlock:^{
    [weakself requestDataForUser];
}];

这篇关于等待异步调用在完成块内完成的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆