php - How to make an asynchronous self-calling loop non-recursive -
i'm writing function in php loops through array, , performs asynchronous call on (using promise).
the problem that, way can make loop happen, letting function call asynchronously. run 100-nested functions problem quick, , change not recur.
function myloop($data, $index = 0) { if (!isset($data[$index])) { return; } $currentitem = $data[$index]; $currentitem()->then(function() use ($data, $index) { myloop($data, $index + 1); }); } for want answer practical perspective (e.g.: rewrite not asynchronous), i'm experimenting functional , asynchronous patterns , want know if possible php.
i've written possible solution in pseudo-code. idea limit number of items running asynchronous @ once using database queue. myloop() no longer directly recursive, instead being called whenever item finishes running. in sample data, i've limited 4 items concurrently (arbitrary value). basically, it's still recursively calling itself, in roundabout way, avoiding situation mentioned of many nested called.
execution flow:
myloop() ---> queue ^ v | | '<-processor <-' <?php //---------- // database //table: config //columns: setting, value //items: active_count, 0 // item_concurrent_max, 4 //table: queue //columns: id, item, data, index, pid, status(waiting, running, finished), locked // --- end pseudo-schema --- <?php // --------------- // itemloop.php // --------------- //sends item , associated data produced myloop() database queue, //to processed (run asynchronous, limited how many can run @ once) function send_item_to_processor($item, $data, $index, $counter) { //insert $item queue table, along $data, $index (if needed), $counter, locked = 0 //status == waiting } //original code, modified remove direct recursion , implement //the queue. function myloop($data, $index = 0, $counter = 0) { if (!isset($data[$index])) { return; } $currentitem = $data[$index]; $currentitem()->then(function() use ($data, $index) { //instead of directly calling `myloop()`, push item //database , let processor worry it. see below. //*if wanted currentitem call specific function after finishing, //you create array of numbered functions , pass function //number along other data.* send_item_to_processor($currentitem, $data, $index + 1, $counter + 1); }); } // --------------- // processor.php // --------------- //handles actual running of items. looks "waiting" item , //executes it, updating various statuses along way. //*called `process_queue()`* function process_new_items() { //select active_count, item_concurrent_max //item_count = total records in queue. done //short-circuit execution of `process_queue()` whenever possible //(which called frequently). if (item_count == 0 || $active_count >= $item_concurrent_max) return false; //select item queue status = waiting , locked = 0 limit 1; //update item set status = running, pid = programpid //update config active_count = +1 //**** asynchronous run item here ****// return true; } //main processor queue. first processes new/waiting items //if can (if many items aren't running), processes //dead/completed items. upon item.status == finished, `myloop()` //called function. still technically recursive call, //avoids out-of-control situations due asynchronous nature. //this function called on timer of sort, such cronjob function process_queue() { if (!process_new_items()) return false; //too many instances running, no need process //check queue table items status == finished or is_pid_valid(pid) == false $numcomplete = count($rows); //update rows locked = 1, in case process_queue() gets called again before //we finish, resulting in item potentially being processed dead twice. foreach($rows $item) { if (is_invalid(pid) || $status == finished) { //and here call myloop(), avoiding strictly recursive //function call. //*not sure `$item` here -- might passed `myloop()`?.* //delete item(s) queue myloop(data, index, counter - 1); //decrease config.active_count $numcomplete } } }
Comments
Post a Comment