/** * Execute loop until *as.break()* is called * @param callable $func loop body callable( as ) * @param string $label optional label to use for *as.break()* and *as.continue()* in inner loops */ public function loop(callable $func, $label = null) { $this->_sanityCheck(); $loop_state = new \StdClass(); $loop_state->model_as = new \FutoIn\RI\AsyncSteps(); $loop_state->inner_as = null; $loop_state->outer_as = null; $loop_state->func = $func; $loop_state->label = $label; $this->add(function ($outer_as) use($loop_state) { $loop_state->outer_as = $outer_as; $create_iteration = function () use($outer_as, $loop_state) { $inner_as = new \FutoIn\RI\AsyncSteps($outer_as->state()); $inner_as->copyFrom($loop_state->model_as); $loop_state->inner_as = $inner_as; $inner_as->execute(); }; $loop_state->model_as->add(function ($as) use($loop_state) { $func = $loop_state->func; $func($as); }, function ($as, $err) use($loop_state) { if ($err === \FutoIn\Error::LoopCont) { $label = $loop_state->label; $term_label = $as->state()->_loop_term_label; if ($term_label && $term_label !== $label) { // Unroll loops continue \FutoIn\RI\AsyncTool::callLater(function () use($loop_state, $term_label) { try { $loop_state->outer_as->continueLoop($term_label); } catch (\Exception $e) { } }); } else { $as->success(); return; } } elseif ($err === \FutoIn\Error::LoopBreak) { $label = $loop_state->label; $term_label = $as->state()->_loop_term_label; if ($term_label && $term_label !== $label) { // Unroll loops and break \FutoIn\RI\AsyncTool::callLater(function () use($loop_state, $term_label) { try { $loop_state->outer_as->breakLoop($term_label); } catch (\Exception $e) { } }); } else { // Continue linear execution \FutoIn\RI\AsyncTool::callLater(function () use($loop_state, $term_label) { try { $loop_state->outer_as->success(); } catch (\Exception $e) { } }); } } else { \FutoIn\RI\AsyncTool::callLater(function () use($loop_state, $err) { try { $loop_state->outer_as->error($err); } catch (\Exception $e) { } }); } $loop_state->model_as->cancel(); $loop_state->model_as = null; $loop_state->func = null; })->add(function ($as) use($create_iteration) { \FutoIn\RI\AsyncTool::callLater($create_iteration); }); $outer_as->setCancel(function ($as) use($loop_state) { $loop_state->inner_as->cancel(); $loop_state->inner_as = null; if ($loop_state->model_as) { $loop_state->model_as->cancel(); $loop_state->model_as = null; } $loop_state->func = null; }); $create_iteration(); }); return $this; }