Search code examples
javascriptmultithreadingcallbackjquery-callback

Populating local array from callback: synchronization issues?


Here's the deal:

I populate a local array in a callback like so:

var datasource = datasources[id];
var contexts = [];

datasource.data($selected.parent().data(), function (items) {
          var dataarr = items.data;
          for (var i = 0; i < dataarr.length; ++i) {
               contexts.push(dataarr[i]);
          }
      });

foo(contexts);

Now, in foo, I run a simple check like:

function foo(contexts) {
    if (contexts.length < 2) {
        return;
    }
}

If I break at the return statement above, contexts.length is in fact, greater than 2. Similarly, if I run the code step by step in a debugger, everything works as expected.

This make me suspect that when not running in a debugger, this code is being executed before the callback has completed.

Fine. But how can I control the execution order? Or, perhaps, is there a better paradigm to go about this if the only way I know can acquire items.data is from within that callback?

Thanks!


Solution

  • I'm not sure about the context here a little bit, but if you want your method foo to be called after the array has been populated, why not call it after the for loop? like so:

    datasource.data($selected.parent().data(), function (items) {
          var dataarr = items.data;
          for (var i = 0; i < dataarr.length; ++i) {
               contexts.push(dataarr[i]);
          }
          foo(contexts);
      });
    

    if you're uncomfortable with that, look at PubSubJS which allows you to emit events and handle them asynchronously. I wrote an article about it's usage here.

    Edit: An example:

    datasource.data($selected.parent().data(), function (items) {
          var dataarr = items.data;
          for (var i = 0; i < dataarr.length; ++i) {
               contexts.push(dataarr[i]);
          }
          PubSub.publish('contextsPopulated', contexts);
      });
    

    and then modify foo as:

    function foo(message, contexts) {
        if (contexts.length < 2) {
            return;
        }
    }
    

    now register foo to be called whenever 'contextsPopulated' is signalled.

    PubSub.subscribe('contextsPopulated', foo);