Функция NodeJs внутри цикла forEach должна быть завершена, прежде чем перейти к следующему элементу - PullRequest
0 голосов
/ 08 апреля 2019

У меня есть скрипт Nodejs, детали которого выглядят так:

1) запрашивает у API список городов, получает массив JSON. используя этот массив, я делаю цикл используя forEach.

2) на каждой итерации (2-й цикл) я снова запрашиваю API, чтобы получить детали (около 100 строк) и вставить их в базу данных mysql.

мой вопрос, как заставить функцию внутри первого цикла (, где получить список городов ) дождаться завершения, прежде чем перейти к следующему элементу (городу). Я хочу сделать цикл последовательным с задержкой.

мой исходный код:

const request = require('request');
var moment = require('moment');
var mysql = require('mysql');

var a = moment('2019-04-01');
var b = moment('2019-04-06');
const sleep = (waitTimeInMs) => new Promise(resolve => setTimeout(resolve, waitTimeInMs));

function timer(ms) {
 return new Promise(res => setTimeout(res, ms));
}

var connection = mysql.createConnection({
    host : 'localhost',
    user : 'user1',
    password : 'password',
    database : 'local'
});

async function getURL(id_city,dates) {

    var url = 'https://localhost/api/format/json/schedule/city/'+id_city+'/date/'+dates;    
    request(url, { json: true }, (err, res, body) => {
      if (err) { return console.log(err); }
      // console.log(body.status);
      var item1 = body.schedule.data.item1;
      var item2 = body.schedule.data.item2;

      connection.connect();
      connection.query('INSERT INTO schedule (city,item1,item2) values ("'+id_city+'","'+task1+'", "'+task2+'")', function (error, results, fields) {
         if (error) throw error;
      });
      // connection.end();    
    });
}


async function getDate(id_city)
{
    var end;
    for (var m = moment(a); m.isBefore(b); m.add(1, 'days')) {
        getURL(id_city,m.format('YYYY-MM-DD'));
        await timer(1000); //making delay           
    }
}

async function main () {    
    var url = 'https://localhost/api/format/json/list_city';
    connection.connect();
    request(url, { json: true }, (err, res, body) => {
      if (err) { return console.log(err); }

          var list_city = body.city; //this is an array
          var counter = 0;
          list_city.forEach(function(city){
              getDate(city.id, function(){

              });//i need this to complete before go to next city

          });
    });//end request url
}

main();

мои ожидания (последовательные):

city1
insert item a done...
insert item b done...
city2
insert item a done...
insert item b done...
insert item c done...
city3
...

Ответы [ 2 ]

2 голосов
/ 08 апреля 2019

Для request и mysql вы можете использовать поддерживаемый пакет Promise, а именно: request-promise и mysql2. Чтобы гарантировать последовательное выполнение, вы можете сделать:

const rp = require('request-promise');
const mysql = require('mysql2/promise');

// then in your getURL function
async function getURL(id_city,dates) {

  var url = 'https://localhost/api/format/json/schedule/city/'+id_city+'/date/'+dates;
  const body = await rp(url, { json: true })
  const item1 = body.schedule.data.item1;
  const item2 = body.schedule.data.item2;

  const connection = await mysql.createConnection({host:'localhost', user: 'root', database: 'test'});
  const [rows, fields] = await connection.execute('INSERT INTO schedule (city,item1,item2) values ("'+id_city+'","'+task1+'", "'+task2+'")');
}

// One await in getDate should do
async function getDate(id_city) {
  var end;
  for (var m = moment(a); m.isBefore(b); m.add(1, 'days')) {
    await getURL(id_city,m.format('YYYY-MM-DD'));
  }
}

Для обработки ошибки с async/await:

try {
  const body = await rp(url, { json: true })
} catch (e) {
  // handle erorr
  console.error(e);
  // or rethrow error: throw e
}

Для эффективности вы можете использовать mysql connection pool, например:

// myPool.js
const mysql = require('mysql2');

// create pool
const pool = mysql.createPool({
  host:'localhost',
  user: 'root',
  database: 'test',
  connectionLimit: 10,
  queueLimit: 0
});
// now get a Promise wrapped instance of that pool
const promisePool = pool.promise();

module.exports = () => promisePool; 

// Then in your getURL
const getPool = require('./myPool');
async function getURL(id_city,dates) {
  ...

  const pool = await getPool();
  const [rows, fields] = await pool.execute('INSERT INTO schedule (city,item1,item2) values ("'+id_city+'","'+task1+'", "'+task2+'")');
  ...

Также рассмотрите возможность использования оператора prepared.

connection.execute('SELECT * FROM `table` WHERE `name` = ? AND `age` > ?', ['Morty', 14]);
1 голос
/ 08 апреля 2019

Используйте цикл for вместо forEach, и на каждой итерации await вызов getDate, так что один вызов getDate всегда завершается до повторного вызова:

for (let i = 0; i < list_city.length; i++) {
  await getDate(city[i]);
  await timer(100); // this will put a delay of at least 100ms between each call
}

Обязательно включите содержащую функцию async, чтобы это работало.

Обратите внимание, что, поскольку getDate возвращает Promise, он, вероятно, не должен принимать обратный вызов - либо цепочка await s, либо then s до конца.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...