Вложены для цикла в сочетании с извлечением данных из API в Nodejs? - PullRequest
0 голосов
/ 03 декабря 2018

Мой сценарий таков: есть API, из которого я хотел бы получить.API возвращает json, у которого есть массив с именем «assets».Этот размер массива всегда будет равен 20. Теперь я называю конечную точку следующим образом:

 fetch(
     `https://api.example.io/api/v1/assets/?offset=${offset}`
 )

, где, если смещение равно 0, оно вернет массив из 0-20 активов, если смещение равно 20,верните 20 к 40 и т. д.

Я хочу проверить 1000 элементов, что означает, что я хотел бы вызвать эту выборку 1000/20 = 50 раз.Всякий раз, когда я вызываю выборку, я хотел бы перебрать эти 20 элементов и вставить их в мою базу данных.Проблема в том, что я не могу сделать что-то вроде этого:

let offset=0;

for(let i = 0; i < 50; i++ {
    fetch(
       `https://api.example.io/api/v1/assets/?offset=${offset}`
    )
    for(let j = 0; j < 20; j++){
    // Insert into DB
    }
    offset+=20;
}

Из-за асинхронной природы JS.Всякий раз, когда я пытаюсь сделать это, он будет вызывать выборку со значением 0 для смещения пару раз, он не будет ждать окончания вложенного цикла for, а затем вызывать его для 20 и более поздних 40 и так далее ...

Как правильно добиться такого поведения?

Ответы [ 5 ]

0 голосов
/ 04 декабря 2018
Promise.all(Array(50).fill(null).map((v,i) => {
    const url = `https://api.example.io/api/v1/assets/?offset=${i*20}`;

    return fetch(url).then(results => {
        for (let result of results) {
            // insert into DB
        }
    );
}).then(() => console.log("done"));
0 голосов
/ 03 декабря 2018

Я получил следующее от одного из моих репозиториев nodeJS, так как он использует асинхронный код для решения очень похожей проблемы:

// Throttling is important because you don't want to
// overload the API
const createThrottle = require('async-throttle');
const throttle = createThrottle(2);

// First push all the links into an array using
// the offset
const links = [];
for (let offset = 0; offset < 100; offset += 20) {
  links.push(`https://api.example.io/api/v1/assets/?offset=${offset}`);
}

// Next create an array of promises by `map`ing
// over the links using `fetch`.
// Notice I've used throttle here to slow down the hits on
// the API
const promises = links.map(link => throttle(async => () {
  const res = await fetch(link);
  return await res.json();
}));

// Once all the promises are complete, iterate over their datasets
Promise.all(promises).then(datasets => {
  // iterate over datasets
});
0 голосов
/ 03 декабря 2018

Почему бы не использовать Promise.all?

const promises = [];

for (let i = 0; i < 10; i++) {
    promises.push(
    fetch(`https://api.example.io/api/v1/assets/?offset=${i}`)
  );
}

Promise.all(promises).then( _results => {
    // All fetch calls have completed at this point. _results will be in array of your fetch results.
    console.log(results);
    // Do db insert here

});

Одна вещь, которую вы могли бы сделать, это сделать function, которое является обещанием.Внутри этой функции он должен выполнить выборку, а затем выполнить вставку БД все в одной функции (используя .then).Если бы вы сделали это таким образом, единственный вызов Promise.all обработал бы все.Если вы этого не сделаете, вам придется повторить обещания снова и вставить эти значения в БД.Это может выглядеть примерно так:

const promises = [];

for (let i = 0; i < 10; i++) {
    promises.push(fetchAndInsert(i));
}

Promise.all(promises).then( _results => {
    console.log(results);

});


function fetchAndInsert(offset) {
    return new Promise( (resolve, reject) => {
    fetch(`https://api.example.io/api/v1/assets/?offset=${i}`).then (_result => {
        // db.insert().then( _result => {
            //resolve();
      //})
    })
  })
}
0 голосов
/ 03 декабря 2018

Вы можете использовать async и ждать.Это должно работать:

async function fetchExample() {
  for (let i = 0; i < 50; i++) {
    let fetchedData = await fetch(
      `https://api.example.io/api/v1/assets/?offset=${offset}`
      );
    for(data of fetchedData) {
      // Insert into DB
    }
  offset+=20;
  }
}
0 голосов
/ 03 декабря 2018

Вместо for..loop вы можете использовать рекурсию или Promises.

Recursion ::

let insertToDB = function (records, cb) {
  if (!records.length) return cb();
  let record = records.shift();//assuming records is an array
  // Insert record into DB1
  insertToDB(records, cb);
};

let loop = function (count, offset, cb) {
 if (!count) return cb();
 fetch(
    `https://api.example.io/api/v1/assets/?offset=${offset}`
 )
 insertToDB(recordsFromFetch, function () {
    offset += 20;
    --count;
    loop(count, offset, cb)
 })
};


loop(50, 0, function () {
  console.log("All Done");
})

Promise :: Я не запускал его, поэтому может быть какая-то синтаксическая ошибка, но вы поняли

let insertToDB = function (record) {
    return new Promise(function (resolve, reject) {
    // Insert record into DB then call resolve
    resolve();
    })
};

let fetchPhase = function (offset) {
    fetch(
    `https://api.example.io/api/v1/assets/?offset=${offset}`
   )
    let dbAll = [];
   for (let j = 0; j < 20; j++) {
    // assuming you get records from fetch , pass record to be added to db in the parameter
    dbAll.push(insertToDB(records[j]))
}
 return Promise.all(dbAll)
};

let all = [];

let offset = 0;

for (let i = 0; i < 50; i++) {
    all.push(fetchPhase(i));
}

Promise.all(all)
.then(function () {
    console.log("ALL DONe");
})
...