同步取数据 NSOperationQueue 或 NSURLSession

Synchronous fetching data NSOperationQueue or NSURLSession

在我的应用中,当我们点击登录按钮时,我们必须进行 3 次服务器调用。

1) will make a server call to get an OAuth token for user credentials
2) with the token from (1) we will get user privileges
3) with the token from (1) and with the valid privilege from (2) we will get the data to be displayed on the page after login in a tableview. 

我对为此采取的方法感到困惑。使用 operationqueue 添加依赖项或使用 NSURLSession 任务是一种好方法吗?

根据堆栈溢出解决方案之一 - Best practices for making a queue of NSURLSessionTasks , NSURLSession 没有任何关于排序请求的逻辑,即使将每个主机的最大连接数设置为 1

,它也只会调用最先完成的 completionBlock

如果有任何其他更好的方法让我知道..

您可以使用 NSURLSession 个任务。首先调用 first api 方法,您将在完成处理程序(块)中得到响应。现在将它存储在完成处理程序中的任何 public 属性 (因为你想再次使用它)。从该完成处理程序调用第二个 api 方法和从第二个方法的完成处理程序,通过传递响应并使用 public 属性 调用第三个 api 方法,其中第一个 api 方法的对象已存储。

仅当响应到达时才调用完成处理程序或块,因此您可以通过这种方式管理您的 api 调用。

希望这会有所帮助:)

归根结底,使用 "completion handlers" 传统方法的 简化 代码版本可能如下所示:

fetchOAuthToken() { (token, error) in
    if let token = token {
        fetchPrivileges(token: token) { (privileges, error) in
            if let privileges = privileges {
               fetchData(token: token, privileges: privileges) { (data, error) in 
                   if let data = data {
                       // ... 
                   }
               }
            }
        }
    }
}

请注意,仅为了简洁起见,代码不包含错误处理,也没有取消方法。

依赖关系是通过 continuations - 即完成处理程序建立的。

另一种利用 "Scala-like" 期货的方法如下所示(使用 Promises 是非常相似的方法):

fetchOAuthToken().flatMap { token in
    fetchPrivileges(token: token).flatMap { privileges in
        fetchData(token: token, privileges).map { data in
            // ...
        }
    }
}.onFailure { error in
    print("Error: \(error)")
}

上面的语句创建了一个由三个组成的任务。

这种方法包含完整的错误处理,尽管它可能并不明显。生产版本与上面的代码片段没有太大区别 - 它可能只添加了一种取消方式。

有一些第三方库实现了类似 Scala 的 futures 或 Promises。

添加取消的一种方法,可能如下所示:

let cr = CancellationRequest()
fetchOAuthToken(ct: cr.token).flatMap { token in
    fetchPrivileges(token: token, ct: cr.token).flatMap { privileges in
        fetchData(token: token, privileges, ct: cr.token).map { data in
            // ...
        }
    }
}.onFailure { error in
    print("Error: \(error)")
}

稍后,您可以取消组合任务(无论它当前正在执行什么):

cr.cancel()

注:

这个问题也可以NSOperations解决。但是,它需要 NSOperation 的三个 sub类 和一个或两个线程安全助手 类 用于 "transfer" Op1 的结果到 Op2 的输入, Op2 的结果作为 Op3 的输入。我估计这将需要大约 500 行代码——对于 SO 的答案来说太多了 ;)

"Scala-like" 期货方法需要第三方库。取消需要另一个 - 或者你自己的实现(这并不难),或者一个提供所有功能的库。