skip to Main Content

I suspect it’s an issue with asynchronousity,
I just can’t get my head around what’s the problem.
So I make multiple api requests and fill a list with the responses.
Then I have a D3.js script charting the data,
which receives undefined though, I substituted it with a simple log here.
(of course the console “evaluates” the values later in real-time, but logging a nodes[0] item is indeed undefined.)
I tested setting a timeout to the subsequent scripts but no avail,
and I couldn’t come up with a callback solution since the request
is itself a callback already.
(I know forEach is the proper method to use here, but I’m also trying to get the assignment functional with an immediate return, which is also something I couldn’t yet make happen)

variables=["pageid1","pageid2","pageid3"];
nodes=[];
variables.map(function(variable){
    FB.api("/"+variable,"GET",{fields:'name,picture.type(large),engagement'},function(response){
        if(!response.error){
            nodes.push(response);
        }
    });
});
console.log(nodes);

3

Answers


  1. I suspect it’s an issue with asynchronousity

    Yes, of course, your console.log can and will fire before the .api finishes. Trying to delay it with a setTimeout is just bad programming (it’s a guessing game).

    Any processing on nodes needs to be within the .api callback. If you are firing multiple async calls and need them all to finish before processing nodes you should be using something like d3.queue. From the docs (bolding mine):

    A queue evaluates zero or more deferred asynchronous tasks with configurable concurrency: you control how many tasks run at the same time. When all the tasks complete, or an error occurs, the queue passes the results to your await callback. This library is similar to Async.js’s parallel (when concurrency is infinite), series (when concurrency is 1), and queue, but features a much smaller footprint: as of release 2, d3-queue is about 700 bytes gzipped, compared to 4,300 for Async.

    In your case the code might look like this (untested):

    var q = d3.queue();
    
    variables.forEach(function(d) {
      q.defer(FB.api, "/" + variable, , "GET", {fields: 'name,picture.type(large),engagement' });
    });
    
    q.await(function(error, response1, response2, response3) {
      if (error) throw error;
      // build out nodes from responses...
    });
    
    Login or Signup to reply.
  2. You can wrap the requests in a promise. then wait for all the promises to be resolved in which case you will have your nodes variable filled with responses.
    you can do it like so:

    nodes=[];
    function sendRequest( variable ) {
        return new Promise ( ( resolve, reject ) => {
            FB.api("/"+variable,"GET",{fields:'name,picture.type(large),engagement'},function(response){
               if(!response.error)
                     resolve( response );
            });
        });
    }
    
    Promise.all( variables.map(function( variable ) {
        return sendRequest( variable ); 
    })).then( values => {
         /* All the responses are solved here */
         nodes = values.splice( 0 );
         console.log(  nodes );
    });
    
    Login or Signup to reply.
  3. Different approach with async/await and some fixes (let, arrow functions, …):

    let variables = ['pageid1', 'pageid2', 'pageid3'];
    cosnt sendRequest = (variable) => {
        return new Promise ((resolve, reject) => {
            FB.api('/' + variable, {fields:'name,picture.type(large),engagement'}, (response) => {
                if(!response.error) {
                    resolve(response);
                } else {
                    reject(response.error);
                }
            });
        });
    };
    
    const getData = async () => {
        let nodes = [];
        for (let i = 0; i < variables.length; i++) {
            let response = await sendRequest(variables[i]);
            nodes.push(response);
        }
        console.log(nodes);
    };
    
    getData();
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search