对于循环进行 HTTPS 地理编码 api 调用并在结果中将坐标输入 SQL 数据库,如何让 Node 的 HTTPS 模块工作?

For of loop to make HTTPS geocoding api call and on result input the coordinates into SQL database, how to get Node's HTTPS module to work?

我有来自 MongoDB 的 13.000 个文档,其中有地址行 + 邮政编码,我正在尝试对每个文档发出请求 Google 的地理编码 API 并获取 LAT + 对他们来说很长,所以我可以让他们动态地出现在地图搜索中。

我设计了以下 for of 循环,我一次测试 10 个项目,但由于写入数据库调用和调用 API 的异步性质,LAT/LONG 来自 HTTPS 请求的坐标最终成为 undefined/unavailable 到 knex 的 INSERT 并且循环似乎一直在继续...

能不能用阻塞的方式写这个?所以除非两个承诺都已解决,否则 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 调用和数据库调用以确保它们独立工作。

只需使用承诺(或回调)

我知道每个人都讨厌 JavaScript,所以这些反惯用转译器和新语言 "features" 的存在是为了让 JavaScript 看起来像 C# 等等,但老实说,它更容易以最初设计的方式使用该语言(否则使用 Go 或其他一些实际上按照您想要的方式运行的语言 - 并且无论如何性能更高)。如果您必须在应用程序中公开 async/await,请将其放在界面上,而不是到处乱扔。

我的 2¢.

我将编写一些伪代码来向您展示这有多么容易:

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();
    });
  });
}

简单。易于阅读。 1:1 代码的外观与实际发生的事情之间的对应关系。不要试图 "force" JavaScript 表现得与设计方式背道而驰。

你学习理解异步代码的时间越长,理解它的时间就越长。

投入学习写作 JavaScript "the JavaScript way"! :D

这是我更新后的功能,可以正常同步运行,一个一个地获取数据并将其添加到数据库,然后再移动到下一个。

我通过自定义@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);
        });
    });
  }