I have a question about JQuery Promise problem. Actually I have a more difficult problem about the JQuery and Ajax that I need to loop a result from Ajax call then nest it to call another Ajax. But I dont know how to set a delay/timeout between the Ajax calls so I can avoid to put much burden on the server. I want to simplify the problem so I can comprehend the promise better. I have a working function to write some console outputs but the output has to be written after delaying for 2 seconds. Here is the code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
|
<script type="text/javascript"> var Makes=[1,2,3,5,8]; $(document).ready(function(){ func1(); }); function func1(){ timeout().then(function(){ //document.body.innerHTML = "First"; console.log("1"); return timeout(); }).then(function(){ //document.body.innerHTML += "<br />Second"; console.log("2"); return timeout(); }).then(function(){ //document.body.innerHTML += "<br />Third"; console.log("3"); return timeout(); }).then(function(){ //document.body.innerHTML += "<br />Fourth"; console.log("4"); return timeout(); }).then(function(){ //document.body.innerHTML += "<br /> Last"; console.log("5"); return timeout(); }); } function timeout(){ var d = $.Deferred(); setTimeout(function(){ console.log("wait for 2 sec!"); d.resolve(); },2000); return d.promise(); } </script> |
And here is the output
|
|
wait for 2 sec! 1 wait for 2 sec! 2 wait for 2 sec! 3 wait for 2 sec! 4 wait for 2 sec! 5 |
The above code actually I borrowed from http://jsfiddle.net/1njL4w1t/. NOTE: Learn carefully also about the promise error handling Problems inherent to jQuery $.Deferred (jQuery 1.x/2.x).
Now I want to replicate the same behavior but I want to use array reduce here. I have tried many things to achieve that by searching through internet but can’t wrap them up in my brain. Then I asked the problem on stackoverflow. Here is my initial code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
|
var Makes=[1,2,3,5,8]; $(document).ready(function(){ //func1(); //WORKS func2(); //NOT WORKS LIKE func1 }); function func1(){ timeout().then(function(){ console.log("1"); return timeout(); }).then(function(){ console.log("2"); return timeout(); }).then(function(){ console.log("3"); return timeout(); }).then(function(){ console.log("4"); return timeout(); }).then(function(){ console.log("5"); return timeout(); }); } function func2(){ /*Makes.reduce(function(Models,Idx){ return timeout().then(function(){ console.log(Idx); return timeout(); //return $.when(timeout()); }); },0);*/ Makes.reduce(function(Models,Idx){ return Models.then(function(){ return timeout().then(function(){ console.log(Idx); return timeout(); //return $.when(timeout()); }); }); },0); } function timeout(){ var d = $.Deferred(); setTimeout(function(){ console.log("wait for 2 sec!"); d.resolve(); },2000); return d.promise(); } |
BUT it didn’t work like I want. Then I got the working solution from one of the stackoverflow user. Here is the answers (yeah I got two working answers)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
|
function func2(){ Makes.reduce(function(Models, Idx) { return Models.then(function () { return timeout().then(function () { console.log(Idx); }); }); //}, $.Deferred().resolve()); }, $.when()); //better than $.Deferred().resolve()? } function func20(){ (function repeat(Idx) { if (Idx >= Makes.length) return; timeout().then(function () { console.log(Makes[Idx]); repeat(Idx+1); }); }(0)); } |
And it’s work like I want. I made a test file (promise-timeout.php) and try it locally http://localhost/works/jquery/promise-timeout.php.
Now I want to make it more difficult by using ajax. I made two functions (‘getCarQueryMake’ and ‘getCarQueryModel’) to call the ajaxs like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
|
function getCarQueryMake(){ var make=$.ajax({ method: "POST", url: url, dataType: 'json', data:{ "type_action" : 'check_make' } }).done(function (result){ //return result.Makes; //make_count=result.Makes.length; //console.log(result); }); return make; } function getCarQueryModel(make_id,make_display){ return function(){ var model=$.ajax({ method: "POST", url: url, dataType: 'json', data:{ "type_action" : 'check_model', "make_id" : make_id } }).done(function (result){ //return result.Makes; console.log(result); model_count=result.Models.length; console.log("Found "+model_count+" models for "+make_display); }); return model; } } |
Here is the url value
I created a new file ‘ajax-json.php’ to call the carquery API.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
|
<?php $callback=date('YmdHis'); switch($_POST["type_action"]){ case 'check_make': $data = '{"Makes":[{"make_id":"ac","make_display":"AC","make_is_common":"0","make_country":"UK"},{"make_id":"allard","make_display":"Allard","make_is_common":"0","make_country":"UK"}]}'; $results=json_decode($data, true); break; case 'check_model': /*$data = '{"Makes":[{"make_id":"ac","make_display":"AC","make_is_common":"0","make_country":"UK"},{"make_id":"allard","make_display":"Allard","make_is_common":"0","make_country":"UK"}]}'; $results=json_decode($data, true);*/ $makeId=$_POST["make_id"]; $url='http://www.carqueryapi.com/api/0.3/?callback='.$callback.'&cmd=getModels&make='.str_replace("\'","%27",$makeId); $ch=curl_init(); curl_setopt($ch,CURLOPT_URL,$url); curl_setopt($ch,CURLOPT_RETURNTRANSFER,1); curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,false); $curl_data=curl_exec($ch); if(!$curl_data){ $err=curl_error($ch); $err_no=curl_errno($ch); $err_desc='{"error":"'.$err.';Error No:'.$err_no.'"}'; return json_decode($err_desc, true); } $curl_data = str_replace($callback."(","",$curl_data); $curl_data = str_replace(");","",$curl_data); $json_data = json_decode($curl_data, true); $results=$json_data; break; } echo json_encode($results); ?> |
Finally after learning more from the previous sample how to setup the promise and the time out, I can manage how to setup the promise to ajax calls and the timeout to delay every ajax calls. Here is the code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
|
<script type="text/javascript"> var url="ajax-json.php"; $(document).ready(function(){ $.when(getCarQueryMake()).then(function(make){ var makes=make.Makes; makes.reduce(function(model,makeIdx){ return model.then(function(){ return timeout().then(function () { console.log(makeIdx.make_display); }) .then(getCarQueryModel(makeIdx.make_id, makeIdx.make_display)); }); //},$.Deferred().resolve()); //WORKS ALSO WITH $.when() },$.when()); }); }); function timeout(){ var d = $.Deferred(); setTimeout(function(){ console.log("waiting for 5 sec!");d.resolve(); },5000); return d.promise(); } function getCarQueryMake(){ ... } function getCarQueryModel(make_id,make_display){ ... } </script> |
I made a local test file (promise-timeout2.php) and url: http://localhost/works/jquery/promise-timeout2.php. Here is the console output:
|
|
waiting for 5 sec! AC Object {Models: Array[8]} Found 8 models for AC waiting for 5 sec! Allard Object {Models: Array[13]} Found 13 models for Allard |
Here is the API url to get Car Models from Car Make: http://www.carqueryapi.com/api/0.3/?callback=1481095214&cmd=getModels&make=abarth
The API response look like:
|
|
{"Models":[{"model_name":"1000","model_make_id":"abarth"},{"model_name":"1000 Bialbero ","model_make_id":"abarth"},{"model_name":"1000 GT","model_make_id":"abarth"},{"model_name":"1000 TC Corsa","model_make_id":"abarth"},{"model_name":"103 GT","model_make_id":"abarth"},{"model_name":"124","model_make_id":"abarth"},{"model_name":"1300","model_make_id":"abarth"},{"model_name":"1500","model_make_id":"abarth"},{"model_name":"1600","model_make_id":"abarth"},{"model_name":"2000","model_make_id":"abarth"},{"model_name":"205","model_make_id":"abarth"},{"model_name":"207","model_make_id":"abarth"},{"model_name":"208","model_make_id":"abarth"},{"model_name":"209","model_make_id":"abarth"},{"model_name":"210","model_make_id":"abarth"},{"model_name":"2200","model_make_id":"abarth"},{"model_name":"2400","model_make_id":"abarth"},{"model_name":"500","model_make_id":"abarth"},{"model_name":"595","model_make_id":"abarth"},{"model_name":"600","model_make_id":"abarth"},{"model_name":"700","model_make_id":"abarth"},{"model_name":"750","model_make_id":"abarth"},{"model_name":"800 Scorpione Coupe Allemano","model_make_id":"abarth"},{"model_name":"850","model_make_id":"abarth"},{"model_name":"A 112","model_make_id":"abarth"},{"model_name":"Bialbero","model_make_id":"abarth"},{"model_name":"Coupe","model_make_id":"abarth"},{"model_name":"Grande Punto","model_make_id":"abarth"},{"model_name":"Lancia 037","model_make_id":"abarth"},{"model_name":"Mono 1000","model_make_id":"abarth"},{"model_name":"Monomille","model_make_id":"abarth"},{"model_name":"Monotipo","model_make_id":"abarth"},{"model_name":"OT","model_make_id":"abarth"},{"model_name":"Renault","model_make_id":"abarth"},{"model_name":"Simca","model_make_id":"abarth"},{"model_name":"Spider Riviera","model_make_id":"abarth"},{"model_name":"Stola","model_make_id":"abarth"}]} |
Okay. Then I expand and add more nest to get Car Serie from Car Model. Also add a delay 5 second between the ajax call to get the Car Serie/Trim. I added a new function ‘getCarQuerySerie’. The function consist of some return function and the delay/timeout. Here is the code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
|
function getCarQuerySerie(){ return function(model){ var models=model.Models; return models.reduce(function(models,modelIdx){ return models.then(function(){ return timeout().then(function () { console.log(modelIdx.model_name ); }) .then(function(){ var serie=$.ajax({ method: "POST", url: url, dataType: 'json', data:{ "type_action" : 'check_serie', "make_id" : modelIdx.model_make_id, "model_id" : modelIdx.model_name } }).done(function (result){ //return result.Makes; console.log(result); //model_count=result.Series.length; //console.log("Found "+model_count+" models for "+make_display); }); return serie; }); }); }, $.when()); } } |
I need to call this function like this
|
|
$(document).ready(function(){ $.when(getCarQueryMake()).then(function(make){ var makes=make.Makes; makes.reduce(function(model,makeIdx){ return model.then(function(){ return timeout().then(function () { console.log(makeIdx.make_display); }) .then(getCarQueryModel(makeIdx.make_id, makeIdx.make_display)) .then(getCarQuerySerie()); }); //},$.Deferred().resolve()); //WORKS ALSO WITH $.when() },$.when()); }); }); |
Modify ajax-json.php file to handle the ajax call
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
|
... case 'check_serie': $makeId=$_POST["make_id"]; $modelId=$_POST["model_id"]; $url='http://www.carqueryapi.com/api/0.3/?callback='.$callback.'&cmd=getTrims&make='.str_replace("\'","%27",$makeId).'&year=-1&model='.str_replace("\'","%27",$modelId); $ch=curl_init(); curl_setopt($ch,CURLOPT_URL,$url); curl_setopt($ch,CURLOPT_RETURNTRANSFER,1); curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,false); $curl_data=curl_exec($ch); if(!$curl_data){ $err=curl_error($ch); $err_no=curl_errno($ch); $err_desc='{"error":"'.$err.';Error No:'.$err_no.'"}'; return json_decode($err_desc, true); } $curl_data = str_replace($callback."(","",$curl_data); $curl_data = str_replace(");","",$curl_data); $json_data = json_decode($curl_data, true); $results=$json_data; break; |
I made a test file (promise-timeout3.php) and the url:Â http://localhost/works/jquery/promise-timeout3.php. Here is the console output (Here I only use one Make. It’s ‘AC’ because the others Makes has many models and series. ‘AC’ only has 8 models):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
|
waiting for 5 sec! AC Object {Models: Array[8]} Found 8 models for AC waiting for 5 sec! 2-Litre Object {Trims: Array[7]} waiting for 5 sec! 427 Object {Trims: Array[1]} waiting for 5 sec! 428 Object {Trims: Array[14]} waiting for 5 sec! Ace Object {Trims: Array[15]} waiting for 5 sec! Aceca Object {Trims: Array[30]} waiting for 5 sec! Aceca-Bristol Object {Trims: Array[1]} waiting for 5 sec! Cobra Object {Trims: Array[20]} waiting for 5 sec! Greyhound Object {Trims: Array[7]} |
Here is the API url to get Car Series/Trims from Car Models: http://www.carqueryapi.com/api/0.3/?callback=1481095214&cmd=getTrims&make=abarth&year=-1&model=1000&full_results=0
Then one of the Car Series/Trims output:
|
|
{"Trims":[{"model_id":"192","model_make_id":"ac","model_name":"Greyhound","model_trim":"","model_year":"1963","model_body":null,"model_engine_position":"Front","model_engine_cc":"1971","model_engine_cyl":"6","model_engine_type":"in-line","model_engine_valves_per_cyl":"2","model_engine_power_ps":"125","model_engine_power_rpm":"5750","model_engine_torque_nm":"179","model_engine_torque_rpm":null,"model_engine_bore_mm":null,"model_engine_stroke_mm":null,"model_engine_compression":null,"model_engine_fuel":"Gasoline","model_top_speed_kph":null,"model_0_to_100_kph":null,"model_drive":null,"model_transmission_type":null,"model_seats":"4","model_doors":"2","model_weight_kg":"1015","model_length_mm":"4580","model_width_mm":"1670","model_height_mm":"1340","model_wheelbase_mm":"2550","model_lkm_hwy":null,"model_lkm_mixed":null,"model_lkm_city":null,"model_fuel_cap_l":"54","model_sold_in_us":"1","model_co2":null,"model_make_display":"AC","make_display":"AC","make_country":"UK"},{"model_id":"181","model_make_id":"ac","model_name":"Greyhound","model_trim":"","model_year":"1962","model_body":null,"model_engine_position":"Front","model_engine_cc":"1971","model_engine_cyl":"6","model_engine_type":"in-line","model_engine_valves_per_cyl":"2","model_engine_power_ps":"125","model_engine_power_rpm":"5750","model_engine_torque_nm":"179","model_engine_torque_rpm":null,"model_engine_bore_mm":null,"model_engine_stroke_mm":null,"model_engine_compression":null,"model_engine_fuel":"Gasoline","model_top_speed_kph":null,"model_0_to_100_kph":null,"model_drive":null,"model_transmission_type":null,"model_seats":"4","model_doors":"2","model_weight_kg":"1015","model_length_mm":"4580","model_width_mm":"1670","model_height_mm":"1340","model_wheelbase_mm":"2550","model_lkm_hwy":null,"model_lkm_mixed":null,"model_lkm_city":null,"model_fuel_cap_l":"54","model_sold_in_us":"1","model_co2":null,"model_make_display":"AC","make_display":"AC","make_country":"UK"},{"model_id":"222","model_make_id":"ac","model_name":"Greyhound","model_trim":"","model_year":"1961","model_body":null,"model_engine_position":"Front","model_engine_cc":"1971","model_engine_cyl":"6","model_engine_type":"in-line","model_engine_valves_per_cyl":"2","model_engine_power_ps":"125","model_engine_power_rpm":"5750","model_engine_torque_nm":"179","model_engine_torque_rpm":null,"model_engine_bore_mm":null,"model_engine_stroke_mm":null,"model_engine_compression":null,"model_engine_fuel":"Gasoline","model_top_speed_kph":null,"model_0_to_100_kph":null,"model_drive":null,"model_transmission_type":null,"model_seats":"4","model_doors":"2","model_weight_kg":"1015","model_length_mm":"4580","model_width_mm":"1670","model_height_mm":"1340","model_wheelbase_mm":"2550","model_lkm_hwy":null,"model_lkm_mixed":null,"model_lkm_city":null,"model_fuel_cap_l":"54","model_sold_in_us":"1","model_co2":null,"model_make_display":"AC","make_display":"AC","make_country":"UK"},{"model_id":"223","model_make_id":"ac","model_name":"Greyhound","model_trim":"","model_year":"1960","model_body":null,"model_engine_position":"Front","model_engine_cc":"1971","model_engine_cyl":"6","model_engine_type":"in-line","model_engine_valves_per_cyl":"2","model_engine_power_ps":"125","model_engine_power_rpm":"5750","model_engine_torque_nm":null,"model_engine_torque_rpm":null,"model_engine_bore_mm":"66.0","model_engine_stroke_mm":"96.0","model_engine_compression":null,"model_engine_fuel":"Gasoline","model_top_speed_kph":null,"model_0_to_100_kph":null,"model_drive":null,"model_transmission_type":null,"model_seats":"4","model_doors":"2","model_weight_kg":"1015","model_length_mm":"4580","model_width_mm":"1670","model_height_mm":"1340","model_wheelbase_mm":"2550","model_lkm_hwy":null,"model_lkm_mixed":null,"model_lkm_city":null,"model_fuel_cap_l":"54","model_sold_in_us":"1","model_co2":null,"model_make_display":"AC","make_display":"AC","make_country":"UK"},{"model_id":"202","model_make_id":"ac","model_name":"Greyhound","model_trim":"","model_year":"1959","model_body":null,"model_engine_position":"Front","model_engine_cc":"1969","model_engine_cyl":"6","model_engine_type":"in-line","model_engine_valves_per_cyl":"2","model_engine_power_ps":"125","model_engine_power_rpm":"5750","model_engine_torque_nm":"179","model_engine_torque_rpm":"4500","model_engine_bore_mm":"66.0","model_engine_stroke_mm":"96.0","model_engine_compression":"9.0:1","model_engine_fuel":"Gasoline","model_top_speed_kph":"167","model_0_to_100_kph":null,"model_drive":"Rear","model_transmission_type":"Manual","model_seats":null,"model_doors":"2","model_weight_kg":"1015","model_length_mm":"4580","model_width_mm":"1660","model_height_mm":"1340","model_wheelbase_mm":"2550","model_lkm_hwy":null,"model_lkm_mixed":null,"model_lkm_city":null,"model_fuel_cap_l":"54","model_sold_in_us":"1","model_co2":null,"model_make_display":"AC","make_display":"AC","make_country":"UK"},{"model_id":"195","model_make_id":"ac","model_name":"Greyhound","model_trim":"2200","model_year":"1959","model_body":null,"model_engine_position":"Front","model_engine_cc":"2216","model_engine_cyl":"6","model_engine_type":"in-line","model_engine_valves_per_cyl":"2","model_engine_power_ps":"105","model_engine_power_rpm":"4700","model_engine_torque_nm":"175","model_engine_torque_rpm":"4700","model_engine_bore_mm":null,"model_engine_stroke_mm":null,"model_engine_compression":null,"model_engine_fuel":"Gasoline","model_top_speed_kph":null,"model_0_to_100_kph":null,"model_drive":null,"model_transmission_type":null,"model_seats":null,"model_doors":"4","model_weight_kg":"1093","model_length_mm":"4580","model_width_mm":"1670","model_height_mm":"1340","model_wheelbase_mm":"2550","model_lkm_hwy":null,"model_lkm_mixed":null,"model_lkm_city":null,"model_fuel_cap_l":"54","model_sold_in_us":"1","model_co2":null,"model_make_display":"AC","make_display":"AC","make_country":"UK"},{"model_id":"154","model_make_id":"ac","model_name":"Greyhound","model_trim":"2600","model_year":"1959","model_body":null,"model_engine_position":"Front","model_engine_cc":"2553","model_engine_cyl":"6","model_engine_type":"in-line","model_engine_valves_per_cyl":"2","model_engine_power_ps":"170","model_engine_power_rpm":"5500","model_engine_torque_nm":null,"model_engine_torque_rpm":null,"model_engine_bore_mm":"82.6","model_engine_stroke_mm":"79.5","model_engine_compression":null,"model_engine_fuel":"Gasoline","model_top_speed_kph":null,"model_0_to_100_kph":null,"model_drive":null,"model_transmission_type":null,"model_seats":null,"model_doors":"4","model_weight_kg":"1040","model_length_mm":"4580","model_width_mm":"1670","model_height_mm":"1340","model_wheelbase_mm":"2550","model_lkm_hwy":null,"model_lkm_mixed":null,"model_lkm_city":null,"model_fuel_cap_l":"54","model_sold_in_us":"1","model_co2":null,"model_make_display":"AC","make_display":"AC","make_country":"UK"}]} |
NEXT. I need to update the local database also from the APIs but I think it’d need more time to make the local database as complete as CarQuery database. Like I said previously, I dont want to burden the CarQuery server from the API requests! May be I need to increase the delay from 5 seconds to 10 seconds each API request??? Also need to add progress bar and how to handle/catch any error???