1

This is a simplified code that runs on Node.js crawler and it gets all data. But how do I insert inside the "callback": function value of var "i" from cycle for(var i=0... When I'm adding name: datas[i].name it returns an error:

TypeError: Cannot read property 'undefined' of undefined

var Crawler = require("crawler").Crawler;
var crawler = new Crawler;

var datas  = [
{name: 'John', url: 'john025'},
{name: 'Jim', url: 'jim04567'}
];

function crauler(done) {
for (var i = 0; i < datas.length; i++) {
    var link = 'http://somesite.com/' + datas[i].url;
    crawler.queue([{
        "uri": link,
            // inside this func
                        "callback": function (error, result, $, datas, i) {
            var arr = $('.blogpost').map(function (index) {
                var str = $(this).attr('href');
                var object = {
                    numb: str,
                    name: datas[i].name
                };
                return obj; 
            }).get().join(',');
            done(arr);
} }]) }; };

crauler (function (arr) {
console.log(arr);
});
khex
  • 2,778
  • 6
  • 32
  • 56
  • declare `i` outside the for loop. – Rosdi Kasim Jun 23 '13 at 15:43
  • @bfavaretto It's not. Looping variable isn't the main issue here for the TypeError, see my answer for details. – Ye Liu Jun 23 '13 at 16:19
  • @jaux The error is caused by an attempt to solve the [infamous loop problem](http://stackoverflow.com/questions/1451009/javascript-infamous-loop-problem), so the dupe link makes sense. BTW, nice answer. – bfavaretto Jun 23 '13 at 16:26
  • @bfavaretto Thank you! But this issue really isn't the infamous loop problem. Infamous loop problem is that you always get the last value of the looping variable, but OP got `undefined` when trying to access `datas` and `i`. – Ye Liu Jun 23 '13 at 16:37
  • @jaux When you remove the arguments that are shadowing the outer variables (as in your answer), then it becomes that problem. And one of the possible solutions is to wrap the callback in an IIFE (as in your answer). So maybe this is not a duplicate, but without a doubt the linked questions are closely related. – bfavaretto Jun 23 '13 at 16:44
  • @bfavaretto After re-reading your comment, I agree with you, OP was indeed trying to solve the loop problem, the infamous loop problem should be linked here. Thank you! – Ye Liu Jun 23 '13 at 16:51

3 Answers3

3

You can't pass datas and i into callback functions like this. What arguments that the callback functions will be called with are up to the caller, you don't have the control of it.

You're seeing "TypeError: Cannot read property 'undefined' of undefined" because you want your callback function to have datas and i as parameters; but the caller will call the callback with the first 3 arguments only [crawler callback reference], so the datas and i are undefined.

Therefore, you should remove the datas and i from in line:

"callback": function (error, result, $, datas, i) {

Because datas is defined in the outer scope of the callback function, the callback can access datas without any special treatment. For the variable i, it's a little bit tricky as mentioned in other answers, so you need to create a closure for it.

So, your callback function definition should be something looks like the following:

"callback": (function(i) { // create closure for i
    return function (error, result, $) { // no more datas and i here
        var arr = $('.blogpost').map(function (index) {
            var str = $(this).attr('href');
            var object = {
                numb: str,
                name: datas[i].name // access datas as it
            };
            return obj; 
        }).get().join(',');
        done(arr);
    }
})(i)
Ye Liu
  • 8,946
  • 1
  • 38
  • 34
  • Thank! All that nuances and nuances. And can you give a link to read about this style of syntax when function placed in () with some arguments after that. I see it for the first time. – khex Jun 23 '13 at 17:10
  • @Ligamentum It's function expression http://stackoverflow.com/questions/1013385/what-is-the-difference-between-a-function-expression-vs-declaration-in-javascrip – Ye Liu Jun 23 '13 at 17:38
2

You're trying to create a closure around i inside of a loop which is causing you problems. This answer should help you:

JavaScript closure inside loops – simple practical example

Community
  • 1
  • 1
sellmeadog
  • 7,437
  • 1
  • 31
  • 45
2

You need a closure to capture the values, this is one way to solve the problem. Read up on closures.

Javascript

var Crawler = require("crawler").Crawler;
var crawler = new Crawler;

var datas = [{
    name: 'John',
    url: 'john025'
}, {
    name: 'Jim',
    url: 'jim04567'
}];

function queue(link, i) {
        crawler.queue([{
            "uri": link,
            // inside this func
            "callback": function (error, result, $, datas, i) {
                var arr = $('.blogpost').map(function (index) {
                    var str = $(this).attr('href');
                    var object = {
                        numb: str,
                        name: datas[i].name
                    };
                    return obj;
                }).get().join(',');
                done(arr);
            }
        }]);
}

function crauler(done) {
    for (var i = 0; i < datas.length; i++) {
        var link = 'http://somesite.com/' + datas[i].url;
        queue(link, i);
};

crauler(function (arr) {
    console.log(arr);
});
Xotic750
  • 22,914
  • 8
  • 57
  • 79