我使用的 API 需要多次请求才能获得搜索结果。之所以这样设计,是因为搜索可能需要很长时间(> 5 分钟)。初始响应会立即返回有关搜索的元数据,并且该元数据将用于后续请求,直到搜索完成。我不控制 API。
search_cookie(一个字符串)和 search_completed_pct(一个 Int)search_cookie 附加到 URL。例如https://api.com/sessions/results/c601eeb7872b7+0 search_completed_pct == 100)search_completed_pct 是搜索进度,介于 0 和 100 之间。search_completed_pct == 100)我在这里发现了很多类似的帖子,很多都使用 Dispatch Groups 和 for 循环,但这种方法对我不起作用。我尝试了一个 while 循环并遇到了变量范围问题。调度组也不适合我。这闻起来像是走错了路,但我不确定。
我正在寻找合适的设计来进行这些递归调用。我应该使用委托(delegate)还是闭包+循环?我遇到了困难,需要一些帮助。
下面的代码是我尝试过的大致思路(为清晰起见进行了编辑。没有 dispatch_groups()、错误处理、json 解析等)
Viewcontroller.swift
apiObj.sessionSearch(domain) { result in
Log.info!.message("result: \(result)")
})
ApiObj.swift
func sessionSearch(domain: String, sessionCompletion: (result: SearchResult) -> ()) {
// Make request to /search/ url
let task = session.dataTaskWithRequest(request) { data, response, error in
let searchCookie = parseCookieFromResponse(data!)
********* pseudo code **************
var progress: Int = 0
var results = SearchResults()
while (progress != 100) {
// Make requests to /results/ until search is complete
self.getResults(searchCookie) { searchResults in
progress = searchResults.search_pct_complete
if (searchResults == 100) {
completion(searchResults)
} else {
sleep(5 seconds)
} //if
} //self.getResults()
} //while
********* pseudo code ************
} //session.dataTaskWithRequest(
task.resume()
}
func getResults(cookie: String, completion: (searchResults: NSDictionary) -> ())
let request = buildRequest((domain), url: NSURL(string: ResultsUrl)!)
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithRequest(request) { data, response, error in
let theResults = getJSONFromData(data!)
completion(theResults)
}
task.resume()
}
最佳答案
好吧,首先,似乎很奇怪没有带有 GET 请求的 API 只返回结果——即使这可能需要几分钟。但是,正如您提到的,您无法更改 API。
因此,根据您的描述,我们需要发出一个有效地“轮询”服务器的请求。我们这样做,直到我们检索到 已完成 的 Search 对象。
因此,一种可行的方法是有意定义以下函数和类:
从服务器返回的“搜索”对象的协议(protocol):
public protocol SearchType {
var searchID: String { get }
var isCompleted: Bool { get }
var progress: Double { get }
var result: AnyObject? { get }
}
在客户端使用具体的结构或类。
向服务器发出请求以创建搜索对象的异步函数(您的#1 POST 请求):
func createSearch(completion: (SearchType?, ErrorType?) -> () )
然后是另一个异步函数,它获取一个“Search”对象,如果完成则可能获取结果:
func fetchSearch(searchID: String, completion: (SearchType?, ErrorType?) -> () )
现在,一个异步函数获取某个“searchID”(您的“search_cookie”)的结果 - 并在内部实现轮询:
func fetchResult(searchID: String, completion: (AnyObject?, ErrorType?) -> () )
fetchResult 的实现现在可能如下所示:
func fetchResult(searchID: String,
completion: (AnyObject?, ErrorType?) -> () ) {
func poll() {
fetchSearch(searchID) { (search, error) in
if let search = search {
if search.isCompleted {
completion(search.result!, nil)
} else {
delay(1.0, f: poll)
}
} else {
completion(nil, error)
}
}
}
poll()
}
此方法使用本地函数 poll 来实现轮询功能。 poll 调用 fetchSearch 并在完成时检查搜索是否完成。如果不是,它会延迟一定的持续时间,然后再次调用 poll。这看起来像一个递归调用,但实际上它不是,因为再次调用时 poll 已经完成。本地函数似乎适合这种方法。
函数 delay 只是等待指定的秒数,然后调用提供的闭包。 delay 可以根据 dispatch_after 或带有可取消调度计时器的方式轻松实现(我们需要稍后实现取消)。
我没有展示如何实现 createSearch 和 fetchSearch。这些可以使用第三方网络库轻松实现,也可以基于 NSURLSession 轻松实现。
结论:
可能会变得有点麻烦的是实现错误处理和取消,以及处理所有的完成处理程序。为了以简洁优雅的方式解决这个问题,我建议使用一个实现“Promises”或“Futures”的辅助库——或者尝试用 Rx 解决它。
例如利用“类 Scala” future 的可行实现:
func fetchResult(searchID: String) -> Future<AnyObject> {
let promise = Promise<AnyObject>()
func poll() {
fetchSearch(searchID).map { search in
if search.isCompleted {
promise.fulfill(search.result!)
} else {
delay(1.0, f: poll)
}
}
}
poll()
return promise.future!
}
您将开始获得如下所示的结果:
createSearch().flatMap { search in
fetchResult(search.searchID).map { result in
print(result)
}
}.onFailure { error in
print("Error: \(error)")
}
以上内容包含完整的错误处理。它还不包含取消。您确实需要实现一种取消请求的方法,否则轮询可能不会停止。
使用“CancellationToken”实现取消的解决方案可能如下所示:
func fetchResult(searchID: String,
cancellationToken ct: CancellationToken) -> Future<AnyObject> {
let promise = Promise<AnyObject>()
func poll() {
fetchSearch(searchID, cancellationToken: ct).map { search in
if search.isCompleted {
promise.fulfill(search.result!)
} else {
delay(1.0, cancellationToken: ct) { ct in
if ct.isCancelled {
promise.reject(CancellationError.Cancelled)
} else {
poll()
}
}
}
}
}
poll()
return promise.future!
}
它可能被称为:
let cr = CancellationRequest()
let ct = cr.token
createSearch(cancellationToken: ct).flatMap { search in
fetchResult(search.searchID, cancellationToken: ct).map { result in
// if we reach here, we got a result
print(result)
}
}.onFailure { error in
print("Error: \(error)")
}
稍后您可以取消请求,如下所示:
cr.cancel()
关于swift - 递归/循环 NSURLSession 异步完成处理程序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35327077/
我需要在客户计算机上运行Ruby应用程序。通常需要几天才能完成(复制大备份文件)。问题是如果启用sleep,它会中断应用程序。否则,计算机将持续运行数周,直到我下次访问为止。有什么方法可以防止执行期间休眠并让Windows在执行后休眠吗?欢迎任何疯狂的想法;-) 最佳答案 Here建议使用SetThreadExecutionStateWinAPI函数,使应用程序能够通知系统它正在使用中,从而防止系统在应用程序运行时进入休眠状态或关闭显示。像这样的东西:require'Win32API'ES_AWAYMODE_REQUIRED=0x0
我脑子里浮现出一些关于一种新编程语言的想法,所以我想我会尝试实现它。一位friend建议我尝试使用Treetop(Rubygem)来创建一个解析器。Treetop的文档很少,我以前从未做过这种事情。我的解析器表现得好像有一个无限循环,但没有堆栈跟踪;事实证明很难追踪到。有人可以指出入门级解析/AST指南的方向吗?我真的需要一些列出规则、常见用法等的东西来使用像Treetop这样的工具。我的语法分析器在GitHub上,以防有人希望帮助我改进它。class{initialize=lambda(name){receiver.name=name}greet=lambda{IO.puts("He
我有多个ActiveRecord子类Item的实例数组,我需要根据最早的事件循环打印。在这种情况下,我需要打印付款和维护日期,如下所示:ItemAmaintenancerequiredin5daysItemBpaymentrequiredin6daysItemApaymentrequiredin7daysItemBmaintenancerequiredin8days我目前有两个查询,用于查找maintenance和payment项目(非排他性查询),并输出如下内容:paymentrequiredin...maintenancerequiredin...有什么方法可以改善上述(丑陋的)代
Rackup通过Rack的默认处理程序成功运行任何Rack应用程序。例如:classRackAppdefcall(environment)['200',{'Content-Type'=>'text/html'},["Helloworld"]]endendrunRackApp.new但是当最后一行更改为使用Rack的内置CGI处理程序时,rackup给出“NoMethodErrorat/undefinedmethod`call'fornil:NilClass”:Rack::Handler::CGI.runRackApp.newRack的其他内置处理程序也提出了同样的反对意见。例如Rack
我想用ruby编写一个小的命令行实用程序并将其作为gem分发。我知道安装后,Guard、Sass和Thor等某些gem可以从命令行自行运行。为了让gem像二进制文件一样可用,我需要在我的gemspec中指定什么。 最佳答案 Gem::Specification.newdo|s|...s.executable='name_of_executable'...endhttp://docs.rubygems.org/read/chapter/20 关于ruby-在Ruby中编写命令行实用程序
exe应该在我打开页面时运行。异步进程需要运行。有什么方法可以在ruby中使用两个参数异步运行exe吗?我已经尝试过ruby命令-system()、exec()但它正在等待过程完成。我需要用参数启动exe,无需等待进程完成是否有任何rubygems会支持我的问题? 最佳答案 您可以使用Process.spawn和Process.wait2:pid=Process.spawn'your.exe','--option'#Later...pid,status=Process.wait2pid您的程序将作为解释器的子进程执行。除
我构建了两个需要相互通信和发送文件的Rails应用程序。例如,一个Rails应用程序会发送请求以查看其他应用程序数据库中的表。然后另一个应用程序将呈现该表的json并将其发回。我还希望一个应用程序将存储在其公共(public)目录中的文本文件发送到另一个应用程序的公共(public)目录。我从来没有做过这样的事情,所以我什至不知道从哪里开始。任何帮助,将不胜感激。谢谢! 最佳答案 无论Rails是什么,几乎所有Web应用程序都有您的要求,大多数现代Web应用程序都需要相互通信。但是有一个小小的理解需要你坚持下去,网站不应直接访问彼此
我尝试运行2.x应用程序。我使用rvm并为此应用程序设置其他版本的ruby:$rvmuseree-1.8.7-head我尝试运行服务器,然后出现很多错误:$script/serverNOTE:Gem.source_indexisdeprecated,useSpecification.Itwillberemovedonorafter2011-11-01.Gem.source_indexcalledfrom/Users/serg/rails_projects_terminal/work_proj/spohelp/config/../vendor/rails/railties/lib/r
刚入门rails,开始慢慢理解。有人可以解释或给我一些关于在application_controller中编码的好处或时间和原因的想法吗?有哪些用例。您如何为Rails应用程序使用应用程序Controller?我不想在那里放太多代码,因为据我了解,每个请求都会调用此Controller。这是真的? 最佳答案 ApplicationController实际上是您应用程序中的每个其他Controller都将从中继承的类(尽管这不是强制性的)。我同意不要用太多代码弄乱它并保持干净整洁的态度,尽管在某些情况下ApplicationContr
我是一个Rails初学者,但我想从我的RailsView(html.haml文件)中查看Ruby变量的内容。我试图在ruby中打印出变量(认为它会在终端中出现),但没有得到任何结果。有什么建议吗?我知道Rails调试器,但更喜欢使用inspect来打印我的变量。 最佳答案 您可以在View中使用puts方法将信息输出到服务器控制台。您应该能够在View中的任何位置使用Haml执行以下操作:-puts@my_variable.inspect 关于ruby-on-rails-如何在我的R