2015-11-03 2 views
1

Я использую cheerio, request и Node.js.Node.js + Cheerio: запрос внутри цикла

Когда я запускаю сценарий ниже, он выводит имена в неправильном порядке. Я считаю, что это вызвано асинхронным характером этого, как я могу заставить его работать в «правильном» порядке? Нужно ли использовать пакет синхронизации или есть способ изменить его таким образом, чтобы он работал синхронно?

app.get('/returned', function (req, res) { 
    for (var y = 0; y < 10; y++) { 
     var url = "http://example.com" + y + "/person.html"; 
     request(url, function (err, resp, body) { 
      $ = cheerio.load(body); 
      var links = $('#container'); 
      var name = links.find('span[itemprop="name"]').html(); // name 
      if (name == null) { 
       console.log("returned null"); 
      } else { 
       console.log(name); 
      } 

     }); 
    } 
}); 
+1

все зависит. Вы хотите отправлять запросы параллельно или последовательно. В серии будет гораздо медленнее, чем параллельно, если сервер, на который вы запрашиваете, может обрабатывать более одного запроса за раз. Его можно обрабатывать параллельно, сохраняя при этом результаты в том порядке, в котором вы хотите. –

+0

Параллельно. Я думаю, что async выполнит эту работу (ответил @ dm03514), я рассмотрю ее. – salep

+1

Обратите внимание, что его ответ работает последовательно, а не параллельно. но у async есть параллельный параметр, вам просто нужно как-то сохранить результаты в том порядке, который вы хотите, независимо от того, что они будут вызваны из строя. –

ответ

5

Promise делает это относительно легко:

app.get('/returned', function (req, res) { 
    let urls = []; 
    for (let y = 0; y < 10; y++) { 
     urls.push('http://example.com' + y + '/person.html'); 
    } 
    Promise.all(urls.map(function (url) { 
     return new Promise(resolve, reject) { 
      request(url, function (err, resp, body) { 
       if (err) {return reject(err);} 
       let $ = cheerio.load(body); 
       let links = $('#container'); 
       let name = links.find('span[itemprop="name"]').html(); // name 
       resolve({name: name, links: links, url: url}); 
      }); 
     }); 
    }).then(function (result) { 
     result.forEach(function (obj) { 
      if (obj.name == null) { 
       console.log(obj.url, "returned null"); 
      } else { 
       console.log(obj.url, obj.name); 
      } 
     }); 
    }).catch(function (err) { 
     console.log(err); 
    }); 
}); 

Я начал создавать массив адресов, чтобы получить, то я сопоставляются, что массив обещаний. Когда каждый из запросов будет завершен, я разрешил обещание с именем, URL-адресом и ссылками. Когда все обещания были завершены, я затем зациклил результат, который будет в первоначальном порядке. Это выполняется параллельно.

+0

спасибо. – salep

+0

Что делает «app.get ('/ return', ...» do? Является ли этот каталог конкретным для этого примера? Я пытаюсь воссоздать что-то подобное. – daneasterman

+0

... он создает маршрут ... –

1

Нет, вам не придется использовать пакет синхронизации. ИМО самым чистым способом является использование зрелой сторонней библиотеки.

Я бы порекомендовал async.

Метод async.series будет выполнять все функции запроса в том порядке, в котором они указаны, а затем разрешить регистрацию обратного вызова для запуска при выполнении всех запросов или при возникновении ошибки.

https://github.com/caolan/async#seriestasks-callback