nodejs同步遍历接口10次该怎么写呢?

想要遍历一个接口10次。但是因为Nodejs天生异步的影响。不会等到每次接口返回后再进行下次循环遍历。请问什么方法可以解决我的这个问题呢?

for(let i=0;i<10;i++){
    //就是想在每次接口返回以后再进行下次循环
    request(queryStringSql + '&page=' +i + '', function (error, response, body){
        if (!error && response.statusCode == 200) {
            cardsList.concat(JSON.parse(body)['cards']);
        }
    });
}

就是想每次for循环都是在上次for循环中访问接口结束以后。就是在for循环中使用同步,不使用异步。

阅读 7.2k
5 个回答

使用BluebirdPromise.mapSeries方法即可。

var Promise = require("Bluebird");
var request = require("request");
var _ = require("underscore");

// 将request模块Promise化
Promise.promisifyAll("request");

// 生成[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
var N = _.range(10);

// 依次顺序遍历N数组
Promise.mapSeries(N, function(i)
{
    return requestAsync(queryStringSql + '&page=' + i + '')
        .then(function(response, body)
        {
            if (response.statusCode == 200)
            {
                cardsList.concat(JSON.parse(body)['cards']);
            }

        });
})
.catch(function(error) {
    console.log(error);
});

如果你用 Node7,可以用 async/await 来写,刚好发了博客从地狱到天堂,还有以前写的 理解 async/await

如果不用 Node7,Async 库的 waterfall() 应该可以处理你的问题,具体参考上述第一篇博客。

也可以自己封装 Promise,然后通过 then 来处理——反正也是不能用 for 的……

并行处理再排序结果

我看了下,我觉得你这个需要可以同时异步去取 10 页的数据,取完之后按一定的标识(页码)进行排序,再按顺序来加工

const requests = Array.apply(null, { length: 10 })
    .map((n, index) => index)
    .map(page => new Promise((resolve, reject) => {
        request(`${queryStringSql}&page=${page}`, function(error, response, body) {
            if (error || response.statusCode != 200) {
                reject(error);
            } else {
                resolve({
                    page: page,
                    body: body
                });
            }
        });
    }));

Promise.all(requests)
    .then(function(results) {
        const sorted = results
            .sort((r1, r2) => r1.page - r2.page)
            .map(r => r.body);
        
        sorted.forEach(body => {
            // 这里已经按页码排好序了,该干啥干啥
        });
    });

async/await

async function doIt() {
    for (var i = 0; i < 10; i++) {
        const body = await new Promise((resolve, reject) => {
            request(`${queryStringSql}&page=${i}`, function(error, response, body) {
                if (error || response.statusCode != 200) {
                    reject(error);
                } else {
                    resolve(body);
                }
            });
        });
        console.log(body);
    }
}

doIt();

其它

Async、Q、Bluebird、co 这些库都有办法实现,但应该都不是用 for 循环。

解决方法很多,这里可以采用q

var Q = require('q');
var request = require('request');

var urls = [
    'http://localhost:3014/q-test/address1',
    'http//localhost:3014/q-test/address2', // this is wrong address
    'http://localhost:3014/q-test/address3',
    'done' // append a useless item
];

function createPromise(url){
    var deferred = Q.defer();
    request(url , function(err , response , body){
        if(err)
            deferred.reject(err);
        else
            deferred.resolve(body);
    });
    return deferred.promise;
}


urls.reduce(function(soFar , url){
   return soFar.then(function(data){
       if(data)
           console.log(data);
       return createPromise(url); // 返回下一个请求的promise
   } ,function(err){
       console.error(err);
       return createPromise(url);// 返回下一个请求的promise
   }) 
},Q(null));

这样就会串行行请求urls数组里面的地址。

具体可以看这篇我写的文章nodejs q模块

或者可以采用ES6里面的generator和co模块来实现

const rp = require('request-promise');
const co = require('co');

let urls = [
    // url1
    // url2
    // url3
];

co(function*(){
    for(let i=0; i<urls.length; ++i){
        var response = yield rp(urls[i]);
        console.log(response);
    }
})

Promise递归调用

reduce就够用了。 具体自己想

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题