草庐IT

ios - Swift - 带循环的多链 http 请求

coder 2023-09-15 原文

自 2 天以来,我感觉我正在搜索整个网络来解决我的多个 http 请求问题。所以我的工作流程是这样的:

  1. 上传图片到服务器

    • 响应 = 带有任务 ID 的 XML 格式
  2. 使用任务 ID 向服务器发出 GET 请求,以检查此任务的状态。

    • 响应 = XML 格式,其中状态可以是“已完成”、“进行中”、“已排队”
    • 如果 Status != "Completed"- 重试第 2 步
    • 如果 Status == "Completed"- 转到第 3 步
  3. 从resultUrl中下载结果

我最后的尝试是使用 PromiseKit 以一种干净的方式链接请求,如本文所述:Chain multiple Alamofire requests .但是如果状态还没有完成,我不知道如何每 2-5 秒循环第二步。

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

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
}

最佳答案

基本思想是编写一个重试相关请求的例程,并且仅在满足特定条件时才履行 promise :

/// 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
}

然后你可以有一个方法来创建满足上述要求的 promise :

/// 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 事情,例如:

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

上面的一些注意事项:

  • 我正在使用 Data 调用 fulfill。通常你会有一些模型对象或类似的东西,但我不确定什么适合你的情况。

  • 我显然没有进行任何 XML 解析。但是您显然会解析响应并相应地确定 taskCompleted

  • 尽管您说任务的状态可以是“已排队”、“进行中”和“已完成”,但我假设您最终会让您的服务器进程在排队任务失败时添加一些错误处理.因此,我还添加了一个 taskFailed 错误。随心所欲。

  • 我对最大重试次数以及出现网络错误时应采取的措施做出了一些假设。显然,根据您的情况自定义这些条件。

所以,不要迷失在我的例子的细节中,而是关注更广泛的画面,即你可以有一个例程来创建一个 Promise 并传递两个 fulfillreject 闭包到实际执行重试逻辑的单独例程。

关于ios - Swift - 带循环的多链 http 请求,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40690726/

有关ios - Swift - 带循环的多链 http 请求的更多相关文章

  1. ruby - 树顶语法无限循环 - 2

    我脑子里浮现出一些关于一种新编程语言的想法,所以我想我会尝试实现它。一位friend建议我尝试使用Treetop(Rubygem)来创建一个解析器。Treetop的文档很少,我以前从未做过这种事情。我的解析器表现得好像有一个无限循环,但没有堆栈跟踪;事实证明很难追踪到。有人可以指出入门级解析/AST指南的方向吗?我真的需要一些列出规则、常见用法等的东西来使用像Treetop这样的工具。我的语法分析器在GitHub上,以防有人希望帮助我改进它。class{initialize=lambda(name){receiver.name=name}greet=lambda{IO.puts("He

  2. ruby-on-rails - 在 Ruby 中循环遍历多个数组 - 2

    我有多个ActiveRecord子类Item的实例数组,我需要根据最早的事件循环打印。在这种情况下,我需要打印付款和维护日期,如下所示:ItemAmaintenancerequiredin5daysItemBpaymentrequiredin6daysItemApaymentrequiredin7daysItemBmaintenancerequiredin8days我目前有两个查询,用于查找maintenance和payment项目(非排他性查询),并输出如下内容:paymentrequiredin...maintenancerequiredin...有什么方法可以改善上述(丑陋的)代

  3. ruby - 如何模拟 Net::HTTP::Post? - 2

    是的,我知道最好使用webmock,但我想知道如何在RSpec中模拟此方法:defmethod_to_testurl=URI.parseurireq=Net::HTTP::Post.newurl.pathres=Net::HTTP.start(url.host,url.port)do|http|http.requestreq,foo:1endresend这是RSpec:let(:uri){'http://example.com'}specify'HTTPcall'dohttp=mock:httpNet::HTTP.stub!(:start).and_yieldhttphttp.shou

  4. ruby-on-rails - Rails HTML 请求渲染 JSON - 2

    在我的Controller中,我通过以下方式在我的index方法中支持HTML和JSON:respond_todo|format|format.htmlformat.json{renderjson:@user}end在浏览器中拉起它时,它会自然地以HTML呈现。但是,当我对/user资源进行内容类型为application/json的curl调用时(因为它是索引方法),我仍然将HTML作为响应。如何获取JSON作为响应?我还需要说明什么? 最佳答案 您应该将.json附加到请求的url,提供的格式在routes.rb的路径中定义。这

  5. ruby - RuntimeError(自动加载常量 Apps 多线程时检测到循环依赖 - 2

    我收到这个错误:RuntimeError(自动加载常量Apps时检测到循环依赖当我使用多线程时。下面是我的代码。为什么会这样?我尝试多线程的原因是因为我正在编写一个HTML抓取应用程序。对Nokogiri::HTML(open())的调用是一个同步阻塞调用,需要1秒才能返回,我有100,000多个页面要访问,所以我试图运行多个线程来解决这个问题。有更好的方法吗?classToolsController0)app.website=array.join(',')putsapp.websiteelseapp.website="NONE"endapp.saveapps=Apps.order("

  6. ruby - 如何验证 IO.copy_stream 是否成功 - 2

    这里有一个很好的答案解释了如何在Ruby中下载文件而不将其加载到内存中:https://stackoverflow.com/a/29743394/4852737require'open-uri'download=open('http://example.com/image.png')IO.copy_stream(download,'~/image.png')我如何验证下载文件的IO.copy_stream调用是否真的成功——这意味着下载的文件与我打算下载的文件完全相同,而不是下载一半的损坏文件?documentation说IO.copy_stream返回它复制的字节数,但是当我还没有下

  7. Ruby 文件 IO 定界符? - 2

    我正在尝试解析一个文本文件,该文件每行包含可变数量的单词和数字,如下所示:foo4.500bar3.001.33foobar如何读取由空格而不是换行符分隔的文件?有什么方法可以设置File("file.txt").foreach方法以使用空格而不是换行符作为分隔符? 最佳答案 接受的答案将slurp文件,这可能是大文本文件的问题。更好的解决方案是IO.foreach.它是惯用的,将按字符流式传输文件:File.foreach(filename,""){|string|putsstring}包含“thisisanexample”结果的

  8. jquery - 我的 jquery AJAX POST 请求无需发送 Authenticity Token (Rails) - 2

    rails中是否有任何规定允许站点的所有AJAXPOST请求在没有authenticity_token的情况下通过?我有一个调用Controller方法的JqueryPOSTajax调用,但我没有在其中放置任何真实性代码,但调用成功。我的ApplicationController确实有'request_forgery_protection'并且我已经改变了config.action_controller.consider_all_requests_local在我的environments/development.rb中为false我还搜索了我的代码以确保我没有重载ajaxSend来发送

  9. ruby - Net::HTTP 获取源代码和状态 - 2

    我目前正在使用以下方法获取页面的源代码:Net::HTTP.get(URI.parse(page.url))我还想获取HTTP状态,而无需发出第二个请求。有没有办法用另一种方法做到这一点?我一直在查看文档,但似乎找不到我要找的东西。 最佳答案 在我看来,除非您需要一些真正的低级访问或控制,否则最好使用Ruby的内置Open::URI模块:require'open-uri'io=open('http://www.example.org/')#=>#body=io.read[0,50]#=>"["200","OK"]io.base_ur

  10. Get https://registry-1.docker.io/v2/: net/http: request canceled while waiting - 2

    1.错误信息:Errorresponsefromdaemon:Gethttps://registry-1.docker.io/v2/:net/http:requestcanceledwhilewaitingforconnection(Client.Timeoutexceededwhileawaitingheaders)或者:Errorresponsefromdaemon:Gethttps://registry-1.docker.io/v2/:net/http:TLShandshaketimeout2.报错原因:docker使用的镜像网址默认为国外,下载容易超时,需要修改成国内镜像地址(首先阿里

随机推荐