Для того, чтобы из цикла сделать HTTPS-геокодирование API-вызовом и по результату ввести координаты в базу данных SQL, как заставить работать HTTPS-модуль Node? - PullRequest
4 голосов
/ 29 марта 2019

У меня есть 13.000 документов из MongoDB, где у меня есть адресная строка + почтовый индекс, я пытаюсь сделать запрос для каждого из них в API геокодирования Google и получить LAT + LONG для них, чтобы они могли динамически отображаться при поиске по карте.

Я разработал следующее для цикла, и я тестирую по 10 элементов за раз, но из-за асинхронного характера как записи в БД, так и вызова API, координаты LAT / LONG изHTTPS-запросы в конечном итоге оказываются неопределенными / недоступными для INSERT knex, и цикл, кажется, продолжает продолжаться и продолжаться ...

Возможно ли написать это блокирующим способом?То есть цикл for не переходит к следующему пункту, если оба обещания не были разрешены?

Код:

let results = [];
  await forLoop();

  async function forLoop() {
    for (job of allJobs) {
      const geoData = await getGeoData(
        job.site.addressLine1,
        job.site.postcode
      );
      const dbResult = await addToDb(geoData);
      results.push(dbResult);

      async function getGeoData(addressLine1, postcode) {
        const friendlyAddress = encodeURIComponent(addressLine1 + ' ' + postcode);
        https
          .get(
            'https://maps.googleapis.com/maps/api/geocode/json?key=<API_KEY_IGNORE_THIS_ITS_HARDCODED_IN_MY_REAL_CODE>&address=' +
              friendlyAddress,
            resp => {
              let data = '';
              resp.on('data', chunk => {
                data += chunk;
              });
              // The whole response has been received. Print out the result.
              resp.on('end', () => {
                console.log(JSON.parse(data).explanation);
                let result = JSON.parse(data);
                return result;
              });
            }
          )
          .on('error', err => {
            console.log('Error: ' + err.message);
          });
      }

      async function addToDb(geoData) {
        try {
          await knex('LOCATIONS')
            .returning('*')
            .insert({
              UPRN: job.site.UPRN,
              lat: geoData.results[0].geometry.location.lat,
              lng: geoData.results[0].geometry.location.lng
            });
        } catch (err) {
          err.name = 'database';
          next(err);
        }
      }
    }
  }
  res.send(results);

Я убедился, что база кода не имеет нулевых значений, и проверил как вызов API, так и вызов базы данных, чтобы убедиться, что они работают изолированно.

Ответы [ 2 ]

1 голос
/ 30 марта 2019

Просто используйте обещания (или обратные вызовы)

Я знаю, что все ненавидят JavaScript, и поэтому эти анти-идиоматические переносчики и новые языковые "возможности" существуют, чтобы JavaScript выглядел как C # и так далее, но, честно говоря, просто проще использовать язык так, как он был изначально разработан ( в противном случае используйте Go или другой язык, который на самом деле ведет себя так, как вы хотите - и в любом случае более производительный). Если вам необходимо выставить async / await в вашем приложении, поместите его в интерфейс, а не засоряйте его повсюду.

My 2 ¢.

Я просто напишу некоторый код psuedo, чтобы показать вам, как легко это может быть:

function doItAll(jobs) {
  var results = [];

  function next() {
    var job = jobs.shift();
    if (!job) {
      return Promise.resolve(results);
    }
    return makeRequest(job.url).then(function (stuff) {
      return updateDb().then(function (dbStuff) {
        results.push(dbStuff);
      }).then(next);
    });
  }

  return next();
}

function makeRequest() {
  return new Promise(function (resolve, reject) {
    var resp = http.get('...', {...});
    resp.on('end', function () {
      // ... do whatever
      resolve();
    });
  });
}

Simple. Легко читать. Соотношение 1: 1 между тем, как выглядит код, и тем, что происходит на самом деле. Не пытайтесь «заставить» JavaScript вести себя не так, как задумывалось.

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

Просто погрузитесь и научитесь писать JavaScript «путем JavaScript»! : D

0 голосов
/ 01 апреля 2019

Вот моя обновленная функция, которая работает правильно и синхронно, получая данные по одному и добавляя их в базу данных, прежде чем перейти к следующей.

Я сделал это, настроив ответ @ coolAJ86, и пометил его как правильный, но подумал, что было бы полезно, чтобы люди, наткнувшись на эту тему, увидели мою окончательную, рабочую и протестированную версию.

var geoApiUrl =
    'https://maps.googleapis.com/maps/api/geocode/json?key=<<MY API KEY>>&address=';

  doItAll(allJobs)

  function doItAll(jobs) {
    var results = [];
    var errors = [];

    function nextJob() {
      var job = jobs.shift();
      if (!job) {
        return Promise.resolve(results);
      }
      var friendlyAddress =
        geoApiUrl +
        encodeURIComponent(job.addressLine1 + ' ' + job.postcode);

      return makeRequest(friendlyAddress).then(function(result) {
        if((result.results[0] === undefined) || (result.results[0].geometry === undefined)){
          nextJob();
        } else { 
        return knex('LOCATIONS')
          .returning('*')
          .insert({
            UPRN: job.UPRN,
            lat: result.results[0].geometry.location.lat,
            lng: result.results[0].geometry.location.lng,
            title: job.title,
            postcode: job.postcode,
            addressLine1: job.addressLine1,
            theo_id: job.clientId
          })
          .then(function(data) {
            // console.log('KNEX CALLBACK COMING')
            // console.log(data[0])
            console.log(data[0]);
            results.push(data[0]);
            nextJob();
          })
          .catch(function(err) {
            console.log(err);
            errors.push(job);
          });
        }
      });
    }
    return nextJob();
  }

  function makeRequest(url) {
    return new Promise(function(resolve, reject) {
      https
        .get(url, resp => {
          let data = '';
          resp.on('data', chunk => {
            data += chunk;
          });
          // The whole response has been received. Print out the result.
          resp.on('end', () => {
            let result = JSON.parse(data);
            resolve(result);
          });
        })
        .on('error', err => {
          console.log('Error: ' + err.message);
          reject(err);
        });
    });
  }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...