1

I am using Node.js with cheerio (a jQuery module on Node.js) and fetch (a module to get HTML body like request) to get some data from a website and save into MongoDB.

I can select the elements like jQuery in browsers

I have to get some data and the pass the data to a callback function, something like:

var data = "";
var fetchById = function(id, callback){
    var data = $("#"+id).parent().siblings(".someClass").text().replace(/\s/g, ""); // line 3
    callback(null, data);
};

and then ...

fetchById(10987, function(err, data){
    if(err){
        // errorHandle(err);
    }else{
        // db.save(data);
    }
});

But sometimes the callback is invoked before line 3 has successfully retrieved data, therefore, data is an empty string ""

How can I ensure that the callback is invoked after line 3 is finished?

kitlee
  • 110
  • 8
  • 6
    javascript runes linearly, it shouldn't run 'callback' before the 3rd line here finishes, the only exception is asynchronous calls, does not look like the case here. – Patrick Nov 25 '14 at 17:58
  • I expect you have done so, but we have to be sure... Have you checked that only your `fetchById` function calls taht callback? And that the only call is made by the context you are currently debugging? – Geeky Guy Nov 25 '14 at 18:03
  • No other call is made in that callback. As mentioned, sometimes I successfully get the data by the jQuery methods, sometimes fail...so I think the only reason is, line 3 needs different length of time on every call – kitlee Nov 25 '14 at 18:22

1 Answers1

0

You can accomplish this by explicit queueing. See jQuery API.

For example (from the API):

$( "#foo" ).slideUp();
$( "#foo" ).queue(function() {
  alert( "Animation complete." );
  $( this ).dequeue();
});

Which is the same as executing .slideUp() first and then, as a callback, executing the alert().

As for you code, this would turn into something like this:

var data = 0;

$("#some_element").queue("yourQueue", function() {
  console.log("First");

  // first function
  data = $("#"+id).parent().siblings(".someClass").text().replace(/\s/g, "");

  $(this).dequeue("yourQueue");
});

$("#some_element").queue("yourQueue", function() {
  console.log("Second");

  // second function
  callback(null, data);    // if callback() is a declared function, this should work

  $(this).dequeue("yourQueue");
});

$("#some_element").dequeue("yourQueue");

Where I used the code from this answer: How do I chain or queue custom functions using JQuery?

Important to note here, is that the .queue() function must be called on the same global element, to make sure the functions are executed (explicitly) sequential.

Community
  • 1
  • 1
Jean-Paul
  • 19,910
  • 9
  • 62
  • 88
  • 1
    The `.queue()` method works for me! but I switch to `jsdom` with jQuery instead of `cheerio` sine it has no `.queue()` method – kitlee Nov 26 '14 at 16:30
  • @WhyCode: Well, if you think I answered your question, consider accepting it. – Jean-Paul Nov 26 '14 at 19:00