locked
jQuery Promises and async calls RRS feed

  • Question

  • User438705957 posted

    I have read that synchronous $.ajax calls will eventually be deprecated, so I am changing all my calls to asynchronous.

    The JavaScript code I have is looping thru an EF model type, identifying which fields are foreign key fields, and building a select/option drop-down that contains the foreign table key and value by linking out to the foreign table in an ajax async call.........simples.
    To do this I am building an array of deferred jQuery promises based on the number of foreign keys in the current model type. Once each promise is completed (promise.Done()), the code kicks in to build the select element.

    Pseudo code is:

    For each foreign key
          var getForeignCollection = getForeignTableData(foreign key field);  // create promise
          deferreds.push(getForeignCollection);  // push promise to array
          getForeignCollection.Done({   // On completion of every promise.
                 build select element 
          })
    End loop of foreign keys

    When all promises are resolved, I employ a $.when.apply($, deferreds) to show a modal with all the drop downs, and also to handle failure.
    $.when.apply($, deferreds)
    .then(function () {  // success in all the deferred promises
         Show modal,
    function (jqXHR, textStatus, err) {  // failure in one of the deferred promises
        Handle failure
    } });

    The problem is the first set of promises in the deferreds array son't seem to be clearing on subsequent calls involving other model types. 
    I am trying to figure out a way to clear/reject the original set of promises for each type.
    If I do a call to the database between calls to this code, it clears itself?

    Thanks for considering.

    Monday, February 25, 2019 10:27 PM

Answers

  • User-474980206 posted

    you can only call resolve or reject on the differed, not the promise. you should never be trying to force this. this code:

    $.when.apply($, deferreds)
    		.then(function () {     // success in all the deferred promises
    			deferreds = [];
    			getForeignCollection.abort;           
    			$('#modalUpdate').modal({ backdrop: 'static', keyboard: false }); 			
    		},

    makes no sense. the then will only be called after the all the promises in the defereds array have called resolve. there is nothing to abort. also not sure the point of clearing the array. 

    note: when you use when().then() with more than one promise, then is only called once, with the values of the first resolve (if all resolve). or first reject (if any reject).

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Wednesday, February 27, 2019 4:41 PM

All replies

  • User839733648 posted

    Hi Madog,

    For each foreign key
          var getForeignCollection = getForeignTableData(foreign key field);  // create promise
          deferreds.push(getForeignCollection);  // push promise to array
          getForeignCollection.Done({   // On completion of every promise.
                 build select element 
          })
    End loop of foreign keys

    Where do you call this function? I think it may affect the result.

    And from the codes using $.when.apply($, deferreds), there seems no any codes to clear the model types already set.

    Maybe you could try Promise .resolve().

    $.when.apply($, deferreds)
    .then(function () {  // success in all the deferred promises
         Show modal,
    function (jqXHR, textStatus, err) {  // failure in one of the deferred promises
        Handle failure
    } });

    I'm also curious about how do you show the modal. What is it content?

    Best Regards,

    Jenifer

    Tuesday, February 26, 2019 8:38 AM
  • User-474980206 posted

    you are building you promise collection, of before done(), try:

    var deferreds = [];
    for (....) {
          var getForeignCollection = getForeignTableData(foreign key field).then(
             function(r) {...},
             function(r) {...}
          );
          // push then promise
          deferreds.push(getForeignCollection); 
    }
    $.when.apply($, deferreds)
       .then(
          ....
       )
       .catch(
          ....
       );
    

    Tuesday, February 26, 2019 4:02 PM
  • User438705957 posted

    Hi Jenifer,

    The foreign key loop is contained in a function called buildUpdateModal().
    This function is called from a click event on an update button at the end of each table row (which is also built in jQuery/JavaScript)

    $(document.body).on("click""tbody tr .update"function () {
    	cleanUp();
    	$('#modalUp').text("Update"); 
    	buildUpdateModal($tr);		
    });

    I just posted the pseudo code for the $.when.apply($, deferreds). Here is the full code with the statement to display the modal.
    The modal contains all the dropdowns for the foreign keys, as well as other fields that are not foreign keys.

    $.when.apply($, deferreds)
    		.then(function () {     // success in all the deferred promises
    			deferreds = [];
    			getForeignCollection.abort;           
    			$('#modalUpdate').modal({ backdrop: 'static', keyboard: false }); 			
    		},
    		function (jqXHR, textStatus, err) {     // failure in one of the deferred promises
    			if (!validationFail) {                
    				errorCallback(jqXHR, textStatus, err);  // call error routine
    				problemPromise();   // disable all clickable controls
    			}            
    			throw "Error";
    		});

    I tried  getForeignCollection.resolve() in the $.then and also in the $.when.
    It doesn't appear that Resolve() is a method property of a jQuery promise and it fails.

    Thanks for responding

    Tuesday, February 26, 2019 11:19 PM
  • User-474980206 posted

    you can only call resolve or reject on the differed, not the promise. you should never be trying to force this. this code:

    $.when.apply($, deferreds)
    		.then(function () {     // success in all the deferred promises
    			deferreds = [];
    			getForeignCollection.abort;           
    			$('#modalUpdate').modal({ backdrop: 'static', keyboard: false }); 			
    		},

    makes no sense. the then will only be called after the all the promises in the defereds array have called resolve. there is nothing to abort. also not sure the point of clearing the array. 

    note: when you use when().then() with more than one promise, then is only called once, with the values of the first resolve (if all resolve). or first reject (if any reject).

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Wednesday, February 27, 2019 4:41 PM
  • User438705957 posted

    Bruce, I'm going to have a go at your form of the code as per your post above, after it stops hitting the fan here.

    The clearing of the deferreds array and the abort were just stabs at it.

    I understand the .then is called after all the promises in the array have called resolve, which is what I want.

    Thanks for responding

    Thursday, February 28, 2019 9:45 PM
  • User438705957 posted

    Ok, So I have it working as expected and how I want.

    The array of deferreds is filled. I then employ a $.when.apply to proceed once all the promises in the array of deferreds have been resolved. 

    if ($tr.data("pathway") && $tr.data("pathway").left(10) === "Adding mtm" && dbField === "Audit_Measure_Task") {	
    	deferreds.push(buildMeasuresNotAdded($tr, objModel));    // Retrieve Audit Measure Tasks not yet added for the audit and build a select element 
    }          
    else {
    	deferreds.push(buildForeignTableSelects(dbField,$tr,objModel));     // Retrieve the foreign collection table for the current foreign key and build a select element
    }
    $.when.apply($, deferreds)      // all promises in the array of deferreds have been resolved one way or the other - show the update modal   
            .then(function () {
                  $('#modalUpdate').modal({ backdrop: 'static', keyboard: false });
    	})		
    	.fail(function (jqXHR, textStatus, err) {     // failure in one of the promises			
    		if (jqXHR.status == 417) {      //417 is ExpectationFailed - this is set by the controller method
    		    errorCallback("Failure", "Failure", jqXHR.responseJSON.CustomErrorMessage);
    		} else {
    		    errorCallback(jqXHR, textStatus, err);
    		    problemPromise();   // disable all clickable controls
    		}
    	}); 

    Each promise in the deferred array has it's own $.Done to process the results of that particular promise.
    I am capturing any failures in the outer .fail under the $.when.

    As Bruce has said, you can't set the status of a promise yourself

    Hope that makes sense.

    Monday, March 25, 2019 3:08 AM