地狱异步函数nodeJs的实践

时间:2017-12-02 14:17:33

标签: javascript node.js asynchronous promise request

我正在开展一个学生项目。我的目标是收集数据并存储它。 我使用带有express的nodeJs,然后在ThemoviesDb上使用superagent运行查询来收集电影,然后将它们存储在neo4j上。

以下是我的例子:

app.get('/', function(req,res){

var hostname = "api.themoviedb.org/3";
var path = "/movie/";



// random id, to store random film  
for(i=0;i<39;i++){
    randomId = Math.floor(Math.random() * 500) + 80;
    console.log(' --------------------- Random id is     ---------------------- ' + randomId);
    superagent.get(hostname + path + randomId)
        .query({ api_key: api_key, append_to_response : 'credits' })
        .end((err, res) => {
            if (err) { 
                if(err.status_code = '34') {
                    return console.log('id : ' + randomId + 'ne correspond pas à un film')
                }
                else{
                    return console.log(err);
                }
            }
            cpt ++;

            title = res.body.original_title;

            if(title){  // Test if title isn't nul, undefined, Nan, empty or 0 to add it
                console.log('randomId --> '  + res.body.id + '--> ' + title );
                if(!Filmadded.includes(film)){
                Filmadded.push(film);
            }       
            }               
        });

    }
                console.log('cpt : ' + cpt + '/39');
res.send('it works' + i );
});

我只是执行一个循环(39因为api的限制是40) 每个我都要求拍电影。

结果:enter image description here

正如您所看到的,我首先显示了所有ID,然后是与ID匹配的标题。 - &GT;我想等到请求结束后再继续。 我看起来很有希望,但我没有一切。

然后我认为我在id / film上的问题是由于这个原因。

感谢您的帮助,对不起我的英语

2 个答案:

答案 0 :(得分:1)

您的superagent()电话是异步的。因此,它不会阻止,您的for循环只会在并行启动所有39个superagent()调用时运行完成。您可以编写代码来并行运行所有这些(如果目标主机允许),但由于您要求能够一个接一个地运行它们,所以这里使用{{的承诺功能实现了您的功能1}}和superagent()序列化await循环内的异步调用,使其运行一个,等待它完成,然后运行下一个:

for

此外,您需要确保您正在声明所有正在使用的变量,因此它们是偶然的全局变量,当其他请求也在运行时可能会发生冲突。

关于此代码看起来不正确的其他事情,但我不知道您的意图:

  1. 变量app.get('/', async function(req, res){ let hostname = "api.themoviedb.org/3"; let path = "/movie/"; // random id, to store random film for (let i = 0; i < 39; i++) { let randomId = Math.floor(Math.random() * 500) + 80; console.log(' --------------------- Random id is ---------------------- ' + randomId); try { let result = await superagent.get(hostname + path + randomId) .query({ api_key: api_key, append_to_response : 'credits' }); cpt ++; let title = result.body.original_title; if (title) { // Test if title isn't nul, undefined, Nan, empty or 0 to add it console.log('randomId --> ' + res.body.id + '--> ' + title ); if (!Filmadded.includes(film)) { Filmadded.push(film); } } } catch(err) { if(err.status_code = '34') { console.log('id : ' + randomId + 'ne correspond pas à un film') } else{ console.log(err); } } console.log('cpt : ' + cpt + '/39'); } res.send('it works' + i ); }); 看起来需要初始化并在某处声明。
  2. 变量cpt可能需要在本地作用域(而不是在服务器上运行多个请求时可能会发生冲突的更高范围的变量)。
  3. Filmadded调用失败时,不清楚您实际上打算如何处理错误。这里只记录错误,但如果您遇到错误,可能需要返回错误状态。

答案 1 :(得分:0)

正如你所说,superagent的get函数是异步的,这意味着事件循环不会在执行下一个命令之前等待函数完成。因此循环启动循环的40次执行,其中包括创建一个随机id,然后使用带有该id的superagent。所以我们谈论两个动作 - 一个是同步的,第二个是异步的。

让我们以另一种方式看待它。假设我们有以下循环:

for(i=0; i<39; i++) {
    const randomId = Math.floor(Math.random() * 500) + 80;
    console.log("RANDOM IS: ", randomId);
    setTimeout(function(){
        console.log("PRINT AGAIN: ", randomId);
    }, 10000);
}

这里有40行“RANDOM IS:[random_number]”连续两次,只有10秒后你才会有40行“PRINT AGAIN:[random_number]”,那就是因为你为第二次记录设置了超时10秒。

您可以将setTimeout与10秒钟的异步函数进行比较 - 仅在异步函数中,您无法确定该函数何时完成。所以基本上你所拥有的与上面的例子类似 - 随机数的40个记录,以及一些随机定时的promises执行。

所以你可能想要考虑的是使用js Array的reduce函数进行链接,或者使用es6 async await function notation

您可以将superagent函数用作承诺,并使用thencatch代替。然后,链接承诺意味着你等待一个承诺完成,然后才执行下一个承诺。