Каков самый элегантный подход к ожиданию окончания цикла и чем делать операцию в Javascript? - PullRequest
0 голосов
/ 31 декабря 2018

У меня есть цикл for, в котором я перебираю набор из примерно 50 объектов, которые я получил от json.В каждой итерации я создаю объект и помещаю его в массив.Мое намерение состоит в том, чтобы после завершения цикла for мой массив также был завершен, и теперь я хотел бы поместить этот массив как часть объекта в коллекцию мангустов.

Проблема, конечно, заключается в том, что при записи его вСпособ синхронизации: я отправлю пустой массив в Mongo, так как он не ждет окончания цикла.То, что я делал раньше, было чем-то безобразным.Я подождал, пока я в цикле for станет array.length - 1, а затем нажал кнопку «Обработка нажатия на часть БД».Проблема здесь в том, что он очень хакерский + он не гарантирует, что я получил все результаты как итерация array.length - 1 может работать до array.length - 3 из-за асинхронной природы JS, но я не возражал против этогоназад чем.

Теперь мне нужно иметь все итерации внутри моего массива.

    fetch("https://api.com/something/"
  .then(r => r.json())
  .then(data => {
    let arrayToFill = [];
    for (let i = 0; i < data.length; i++) {
        let objectToFillTheArrayWith;
        objectData = data[i];
        // Inside the for loop I make an async call to my DB for info
        Game.findOne({ "address": objectData.address })
          .then(existingGame => {
              if (!existingGame) {
                objectToFillTheArrayWith = {
                  title: objectData.name,
                  description: objectData.description,
                  image: objectData.featured_image_url,
                  price: objectData.stats.market_cap
                }
                arrayToFill.push(objectToFillTheArrayWith);
              }
          })
          .catch(err => {
            next(err)
          });
      }       
    let searchObjectDocument = {
      name: "Games",
      results: arrayToFill
    }
    // As you can understand, right now the results key value is an empty array
    new SearchObject(searchObjectDocument).save();
    // ...

Есть ли известное и интуитивно понятное решение для этого?Что-то не очень хакерское, когда я жду, пока я получу значение array.length -1, или, может быть, даже инициирую счетчик и подожду, пока счетчик не станет равным длине массива -1 и т. Д.

Спасибо

Ответы [ 3 ]

0 голосов
/ 31 декабря 2018

Лучше всего использовать библиотеку async .Очень простая и популярная библиотека.

Это пример того, как вы можете ее использовать

const async = require('async')

fetch("https://api.com/something/"
  .then(r => r.json())
  .then(data => {
    let arrayToFill = [];
    async.forEach(data, function(item, callback){
        let objectToFillTheArrayWith;

        Game.findOne({ "address": item.address })
          .then(existingGame => {
              if (!existingGame) {
                objectToFillTheArrayWith = {
                  title: item.name,
                  description: item.description,
                  image: item.featured_image_url,
                  price: item.stats.market_cap
                }
                arrayToFill.push(objectToFillTheArrayWith);
              }

             // When the async is completed you should call callback where the first parameter is an error
             callback(null)

          })
          .catch(err => {
            // In case of an error callback with error
            callback(err)
          });
    }, function(err){
        // Your loop have completed
        if(err) {
            // The loop completed with an error, handle your error
        } else {
            // Your loop was completed successfully
            let searchObjectDocument = {
                name: "Games",
                results: arrayToFill
            }
            // As you can understand, right now the results key value is an empty array
            new SearchObject(searchObjectDocument).save();
            // ...
        }
    })    

async - отличная библиотека, обучение которой не повредит вообще, на самом деле это будеточень выгодно.

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

Каждый раз, когда вы идете в цикл вместо Game.findOne()...Вы бы сделали толчок к массиву обещаний.

Что-то вроде этого (СОХРАНИТЕ, ЧТО ЭТО НЕ ПОЛНЫЙ КОД, ЕГО ПРОСТО ПРИМЕР)

var promises = []
promises.push(Game.findOne()...)

// Then you do a promise.all
Promise.all(promises )
    .then(function(values) {
       // Everything was completed successfully
    })
    .catch(function(err) {
       // There was an error with one or all promises handle it here
    })
});

Не забудьте resolve и reject в каждом обещании

Это хорошая статья о Medium относительно обещаний

0 голосов
/ 31 декабря 2018

Ключом к этому вопросу, а также ко всем этим видам вопросов о СО, является взгляд на лес, а не на деревья.Начиная с верхнего уровня, все, что вам действительно нужно, это:

fetch("https://api.com/something/")
.then(r => r.json())
.then(data => {
    // convert data into a list of games
}).then(games => {
    let searchObjectDocument = {
        name: "Games",
        results: games
    }
    return new SearchObject(searchObjectDocument).save();
}).then( // ...

Теперь, когда вы можете видеть, как будет выполняться выполнение, вы можете заполнить пробел - как преобразовать массив объектов «данных»в массив результатов запроса?Надеемся, что вы используете Bluebird, и в этом случае Promise.map является самым простым способом:

fetch("https://api.com/something/")
.then(r => r.json())
.then(data => {
    return Promise.map(data, row => {
        return Game.findOne({ "address": row.address })
        .then(existingGame => {
            if (!existingGame) {
                return {
                  title: row.name,
                  description: row.description,
                  image: row.featured_image_url,
                  price: row.stats.market_cap
                };
            }
        });
    });
}).then(games => {
    // At this point "games" is an array, but some entries
    // are undefined (the ones where gameExisted already).
    games = games.filter(game => game);
    let searchObjectDocument = {
        name: "Games",
        results: games
    }
    return new SearchObject(searchObjectDocument).save();
}).then( // ...

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

0 голосов
/ 31 декабря 2018

Вы можете использовать карту вместо цикла for, внутри карты, возвращающей обещание, а затем после карты выполните Promise.all (mapResult).Таким образом, обещание. Все будут ждать разрешения каждого обещания.

Как-то так https://stackoverflow.com/a/53982910/9661304

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