在回调中调用异步函数

时间:2013-05-18 20:06:59

标签: javascript node.js asynchronous

我在理解异步函数时遇到了一些麻烦。我已经阅读了Mixu's Node Book中的章节,但我仍然无法绕过它。

基本上我想请求一个资源(使用节点包cheerio),解析它以获取有效的URL并将每个匹配添加到我的redis集setname

问题是,最后它只是将第一个匹配添加到redis集。

function parse(url, setname) 
{
    request(url, function (error, response, body) 
    {
        if (!error && response.statusCode == 200) 
        {
            $ = cheerio.load(body)

            // For every 'a' tag in the body
            $('a').each(function() 
            {
                // Add blog URL to redis if not already there.
                var blog = $(this).attr('href')
                console.log("test [all]: " + blog);

                // filter valid URLs
                var regex = /http:\/\/[^www]*.example.com\//
                var result = blog.match(regex);
                if(result != null) 
                {
                    console.log("test [filtered]: " + result[0]);

                    redis.sismember(setname, result[0], function(err, reply) 
                    {
                        if(!reply) 
                        {
                            redis.sadd(setname, result[0])
                            console.log("Added " + result[0])
                        }
                        redis.quit()    
                    })
                }
            })
        }
    })
}

我非常感谢有关如何重构这一点的指示,因此redis.sadd方法正在使用正确的结果。

当前实现的输出如下:

test [all]: http://test1.example.com/
test [filtered]: http://test1.example.com/
...
Added http://test2.example.com/

所以它添加了test1.example.com但没有打印“添加”行,并且它没有添加test2.example.com,但它正在打印“添加”行。

谢谢!

1 个答案:

答案 0 :(得分:2)

第一个问题是由redis.sismember()异步引起的:当调用它的回调时,你已经覆盖了result变量,因此它将指向它的最后一个值,而不是它的值你拨打redis.sismember()时的那一刻。

解决这个问题的一种方法是通过将异步函数包装在闭包中来创建一个新的作用域变量:

(function(result) {
  redis.sismember(setname, result[0], function(err, reply) {
    ...
  });
})(result);

另一种选择是创建一个用作回调的部分功能:

  redis.sismember(setname, result[0], function(result, err, reply) {
    ...
  }.bind(this, result));

我认为第二个问题是由redis.quit()被调用引起的,它在第一个sadd()之后关闭了Redis连接。您没有检查err,但如果您这样做,可能会告诉您更多信息。