Search code examples
javascriptasynchronouspromisecallbackdojo

Using dojo/Deferred with asychronous functions and queries


I am having a problem getting some queries and functions to run in the correct order. Suppose I have 2 functions that create 2 dropdowns (dropdown_a, dropdown_b). Sequentially create_a(items) must first be called before create_b(items). In fact, create_a(items) creates dropdown_a and then contains an on('change') event that triggers function_b(items), which in turn, creates dropdown_b. To get the parameter items for each of these functions, I first must query a REST service. Without pasting a ton of code (it's huge), the workflow would look this:

function create_a(items):
    var projectNode = dom.byId("projectDropdown");
    ...
    var projectSelector = new Select({
        name: "projectSelect",
        id: "projectSelect",
        options: projectsOptions
    }).placeAt(projectNode).startup();
    dijit.byId('projectSelect').on('change', function (e) {
        var subprojectQuery = new esriQuery();
        ... // creates a query to get subproject items
        subprojectAccessQueryTask.execute(subprojectQuery,
            function (results) {
                let records = results.features;
                createSubprojectDropdown(records); // calls the subproject function
            },
            function (error) {
                console.log("query error: ", error);
            }
        }); // End of deferred Query Task for Project Name
    });
}

function create_b(items) {
    var subprojectNode = dom.byId("subprojectDropdown");
    var p = registry.byId('subprojectSelect');
    if (p) {
        p.destroyRecursive();
    }
    var subprojectSelector = new Select({
        name: "subprojectSelect",
        id: "subprojectSelect",
        options: subprojectsOptions
    }).placeAt(subprojectNode).startup();

    dijit.byId('subprojectSelect').on('change', function (e) {
        thisWidget.loadData(e, proj_type_obj);
    });
    thisWidget.loadData();
}

// Query 1 - Projects
var queryTask_a = new QueryTask();
queryTask_a.execute(query_a_obj,
    function (results) {
        ...
        create_a(results);
        ...
    }
});
// Query 2 Subprojects
var queryTask_b = new QueryTask();
queryTask_a.execute(query_a_obj,
    function (results) {
        ...
        create_b(results);
        ...
    }
});

Now I want to trigger the Projects dropdown on('change') event based on a value. When that is complete, I want to do the same for the subprojects dropdown. Like this:

var info_proj_name = "project_a";
var subproj_name = "subproject_b"
dijit.byId('projectSelect').set("value", info_proj_name);
dijit.byId('subprojectSelect').set("value", subproj_name);     

The problem is, at this point, dijit.byId('projectSelect') may/may not exists in the dom, .set may/may not be a function, etc...It seems that if I run it as above slowly in Chrome Devtools it works but when I let the application run as-is, there seems to be a lag in the time the dropdowns are created and populated by the query and when the dijit.byId('projectSelect').set("value", info_proj_name); gets called. Since I am using Dojo/Dijit already, I am trying to apply a promise/callback functionality like Deferred to run everything in order:

1) query a (get list of items for project dropdown)
2) call create_a function (create project dropdown, trigger subproject process)
3) query b (get list of items for subproject dropdown)
4) call create_b function (create subproject dropdown)
5) select an option from projects dropdown (triggers subprojects query and subrprojects dropdown)
6) select and option from subprojects dropdown (triggers subprojects on(change) event)

I am somewhat confused as to how I could apply something like this to chain these things together. Any suggestions on how to set this up in a general sense?


Solution

  • To avoid having to re-architecture the whole thing, you might try with dojo/ready (see here for documentation):

    ready(function(){
        dijit.byId('projectSelect').set("value", info_proj_name);
        ready(function(){
            dijit.byId('subprojectSelect').set("value", subproj_name); 
        });
    });
    

    If that does not work, the next possibility is setTimeout:

    setTimeout(function(){
        dijit.byId('projectSelect').set("value", info_proj_name);
        setTimeout(function(){
            dijit.byId('subprojectSelect').set("value", subproj_name); 
        }, timeout1);
    }, timeout2);
    

    then do the guess work on timeout1 and timeout2, and cross your fingers above all if those statements need to wait until a server response.

    Now of course re-architecturing will help. I would start with moving the call to queryTask_b.execute inside the queryTask_a.execute callback, and after the create_a statement, which respects your 1, 2, 3, 4 order above.