multithreading - What is the iOS (or RubyMotion) idiom for waiting on a block that executes asynchronously? -
i have been pulling hair out weeks on niggling problem, , can't find info or tips on how or do, i'm hoping here on rubymotion forums can me out.
apologies in advance if little long, requires setup explain issues. background, i've got app uses json/rest back-end implemented ina rails app. pretty straightforward stuff. back-end working fine, , point, front end. can make calls populate model objects in rubymotion client , great.
the 1 issue of http/json libs use async calls when processing requests. fine, , understand why doing it, there couple of situations need able wait on call because need returned results before proceeding next step.
consider example user wants make payment, , have saved payment information. prior presenting user list of payment options, want make sure have latest list @ client. need make request method on user object grab current list (or timeout). don't want continue until sure list either current, or call back-end has failed. basically, want call block (without blocking ui) until results returned.
alternatives such polling changes or pushing changes back-end front not appropriate scenario. considered pulling data destination form (rather pushing form) doesn't work in particular scenario because want different things depending on whether user has zero, 1 or multiple payment options saved. need know in advance of pushing next controller, , know in advance, need make synchronous call.
my first attack create shared instance (let's call synchelper) can use store returned result of request, along "finish" state. can provide wait method spins using cfrunloopruninmode either until request finished, or until request times out.
synchelper looks bit (i've edited take irrelevant stuff out):
class synchelper attr_accessor :finished, :result, :error def initialize() reset end def reset @finished = false @result = nil @error = nil end def finished? @finished end def finish @finished = true end def finish_with_result(r) @result = r @finished = true end def error? !@error.nil? end def wait timeout = 0.0 while !self.finished? && timeout < api_timeout cfrunloopruninmode(kcfrunloopdefaultmode, api_timeout_tick, false) timeout = timeout + api_timeout_tick end if timeout >= api_timeout && !self.finished? @error = "error: timed out waiting api: #{@error}" if !error? end end end
then have helper method this, allow me make call synchronous via provision of syncr instance invoked method.
def apihelper.make_sync(&block) syncr = apihelper::synchelper.new bubblewrap::reactor.schedule block.call syncr end syncr.wait syncr.result end
what had hoped use async versions everywhere, in small number of cases needed synchronously, wrap call around make_sync block this:
# happens async , don't care user.async_call(...) result = apihelper.make_sync |syncr| # 1 async default, need wait completion user.other_async_call(...) |result| syncr.finish_with_result(result) end end # result (after checking errors, etc) result.do_something(...)
importantly, want able return value 'synchronised' call invoking context, hence 'result =...' bit. if can't that, whole thing isn't use me anyway. passing in syncr, can make a call finish_with_result tell listening async task has completed, , store result there consumption invoker.
the problem make_sync , synchelper implementations stand (apart obvious fact i'm doing profoundly stupid) code inside bubblewrap::reactor.schedule ... end block doesn't called until after call syncr.wait has timed out (note: not finished, because block never gets chance run, , hence can't store result in it). starving other processes access cpu, tho call cfrunloopruninmode happening inside wait. under impression cfrunloopruninmode in config spin wait, allow other queued blocks run, appears i've got wrong.
this strikes me people need time-to-time, can't person having trouble kind of problem.
have had many crazy pills? there standard ios idiom doing i'm not understanding? there better way solve kind of problem?
any appreciated.
thanks in advance,
m@
when need display payment options, display hud, mbprogresshud block user using ui , start network call. when network call returns, dismiss hud in either in success/failure blocks or in delegate methods , refresh view data received.
if don't hud idea can display appropriate in ui, uilabel "loading..." or uiactivityindicatorview.
if need data display first thing, in viewdidappear; if happens on action move transition next view (performseguewithidentifier or whatever) network success block/callback , make network call when action called.
there should examples in networking library of how, or take @ usage sample code in mbprogresshud https://github.com/jdg/mbprogresshud.
Comments
Post a Comment