Swift-带循环的多个Chain HTTP请求 [英] Swift - multiple Chain http request with loop

查看:88
本文介绍了Swift-带循环的多个Chain HTTP请求的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

自2天以来,我一直在搜索整个网络以解决多个HTTP请求所带来的问题。所以我的工作流程如下:

Since 2 days it feels that I'm searching the whole web to solve my problem with multiple http requests. So my workflow looks like this:


  1. 将图像上传到服务器

  1. Upload a image to a server


  • 响应=具有任务ID的XML格式

使用以下命令向服务器获取请求

GET request to the server with the Task ID to check the status of this task.


  • Response = XML格式,状态可以为已完成,进行中 ,已排队

  • 如果Status!=已完成-重试步骤2

  • 如果Status == Completed-转到步骤3

从resultUrl下载结果

Download the result from the resultUrl

我的最后一次尝试是使用 PromiseKit 以干净的方式链接请求,如本文中所述:链接多个Alamofire请求。但是,如果状态尚未完成,我不知道如何每2-5秒循环第二步。

My last try was to use PromiseKit to chain the requests in a clean way like described in this post: Chain multiple Alamofire requests. But I don't know how to loop the second step every 2-5 seconds if the status isn't completed yet.

此工作流程是否有推荐的解决方案?这是我用 PromiseKit 进行的测试,在该测试中,我成功地链接了请求,没有循环:

Is there a recommended solution for this workflow? This was my test with PromiseKit, where i successfully chained the requests without a loop:

let request = Client.imageUploadRequest(image: imageView.image!)
let httpOperation = HTTPOperation(withRequest: request)

httpOperation.sendRequest().then() { result -> Promise<String> in
    let xml = SWXMLHash.parse(result)
    let id = self.getXMLAttribute(from: xml, with: "id")!
    let taskStatusrequest =  Client.getTaskStatusRequest(withTaskID: id)
    let httpOperation = HTTPOperation(withRequest: taskStatusrequest)

    return httpOperation.sendRequest()
}
// Loop this result if status != "Completed"
.then { result -> Promise<Data> in
    let xml = SWXMLHash.parse(result)
    let downloadUrl = self.getXMLAttribute(from: xml, with: "resultUrl")!
    let downloadRequest = Client.getDownloadRequest(withUrl: downloadUrl)
    let httpOperation = HTTPOperation(withRequest: downloadRequest)

    // if status != "Completed" don't return, retry this step
    return httpOperation.downloadData()
}
.then { _ -> Void in
    // Refresh View with data
}


推荐答案

基本思想是编写一个重试有问题的请求的例程,并且仅在满足某些条件的情况下才兑现承诺:

The basic idea would be to write a routine that retries the request in question, and only fulfills the promise if certain criteria are met:

/// Attempt a network request.
///
/// - Parameters:
///   - request:    The request.
///   - maxRetries: The maximum number of attempts to retry (defaults to 100).
///   - attempt:    The current attempt number. You do not need to supply this when you call this, as this defaults to zero.
///   - fulfill:    The `fulfill` closure of the `Promise`.
///   - reject:     The `reject` closure of the `Promise.

private func retry(_ request: URLRequest, maxRetries: Int = 100, attempt: Int = 0, fulfill: @escaping (Data) -> Void, reject: @escaping (Error) -> Void) {
    guard attempt < maxRetries else {
        reject(RetryError.tooManyRetries)
        return
    }

    Alamofire.request(request)
        .validate()
        .responseData { response in
            switch response.result {
            case .success(let value):
                let taskCompleted = ...          // determine however appropriate for your app
                let serverReportedFailure = ...

                if serverReportedFailure {
                    reject(RetryError.taskFailed)
                } else if taskCompleted {
                    fulfill(value)
                } else {
                    DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
                        self.retry(request, maxRetries: maxRetries, attempt: attempt + 1, fulfill: fulfill, reject: reject)
                    }
                }
            case .failure(let error):
                reject(error)
            }
    }
}

/// Error codes for retrying of network requests.

enum RetryError: Error {
    case tooManyRetries
    case taskFailed
}

然后,您可以使用一种方法来创建上述条件所满足的承诺:

You can then have a method that creates the promise that is satisfied by the above:

/// Create a promise for a network request that will be retried until
/// some criteria is met.
///
/// - Parameter request: The request to be attempted.
/// - Returns: The `Promise`.

private func retry(for request: URLRequest) -> Promise<Data> {
    return Promise { fulfill, reject in
        self.retry(request, fulfill: fulfill, reject: reject)
    }
}

您现在可以使用上述方法完成标准的 Promise 任务,例如:

You can now do the standard Promise stuff with the above, e.g.:

retry(for: request).then { data in
    print("received \(data)")
}.catch { error in
    print("error: \(error)")
}

上面的一些警告:


  • 我在呼叫 fulfill Data 。通常您会拥有一些模型对象之类的东西,但是我不确定哪种情况适合您。

  • I'm calling fulfill with a Data. Usually you'd have some model object or the like, but I'm not sure what was appropriate in your case.

我显然没有做任何事情XML解析。但是您显然可以解析响应并相应地确定 taskCompleted

I'm obviously not doing any XML parsing. But you'd obviously parse the response and determine taskCompleted accordingly.

尽管您说的是该任务可以排队,进行中和完成,我假设如果排队的任务失败,您最终将让服务器的进程添加一些错误处理。因此,我还添加了 taskFailed 错误。

Although you said the status of the task could either be "queued", "in progress", and "completed", I assumed you'll eventually have your server's process adding some error handling if the queued task failed. So, I also added a taskFailed error. Do as you see fit.

我对最大重试次数以及出现网络错误时的处理方法做了一些假设。显然,将这​​些条件定制为适合您的情况。

I made some assumptions about maximum number of retries and what to do if there was a network error. Clearly, customize these conditions to whatever is appropriate in your case.

因此,请不要在细节中迷失方向在我的示例中,而是更广泛地关注,即您可以创建一个创建 Promise 并传递两个 fulfill 拒绝关闭到一个实际执行重试逻辑的单独例程。

So, don't get lost in the details of my example, but rather focus on the broader picture, namely that you can have a routine that creates a Promise and passes the two fulfill and reject closures to a separate routine that actually performs the retry logic.

这篇关于Swift-带循环的多个Chain HTTP请求的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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