用于异步迭代大文件的生成器

时间:2017-06-13 11:09:35

标签: javascript asynchronous generator postgis idioms

假设我有一个名为openShapeFile的函数,它读取一个文件,并生成一个Promise,它包含一个具有read函数的源对象,该函数返回一个Promise,它将实际值包装在Shapefile,并且有一个.done布尔值,可用于判断文件的末尾是否已到达。

实际上,shapefile.open来自此处: https://www.npmjs.com/package/shapefile

如果我现在想要将文件读入数据库,我可以说:

openShapeFile(`shapefile.shp`).then((source) => source.read()
  .then(function log(result) {
    if (result.done) {
      return
    } else {
      let query = `INSERT INTO geodata(geometry, id, featcode) VALUES(ST_GeomFromGeoJSON('${
        JSON.stringify(Object.assign({}, result.value.geometry, {coordinates: result.value.geometry.coordinates.map(JSON.stringify)}))
      }'), '${
        result.value.properties.ID
      }', ${
        result.value.properties.FEATCODE
      });`
      query = query.split('"[[').join('[[').split(']]"').join(']]')
      return pool.query(query).then((result) => {
        return source.read().then(log)
      })
      }
      })).then(() => console.log(dirCount)).catch(err => 'Problem here')))

这只是工作,但有一个递归的Promise(怪异)

因此,作为练习和/或看看它是否会产生更多的清晰度,我决定将其重写为生成器,产生类似的东西:

function *insertQuery(query) {
    const result = pool.query(query)
    return result
  }

  const shapeFileGenerator = co.wrap(function* (source) {
    while (true) {
      const result = yield source.read()
      if (result.done) {
        return yield {}
      } else {
        let query = `INSERT INTO geodata(geometry, id, featcode) VALUES(ST_GeomFromGeoJSON('${
          JSON.stringify(Object.assign({}, result.value.geometry, {coordinates: result.value.geometry.coordinates.map(JSON.stringify)}))
        }'), '${
          result.value.properties.ID
        }', ${
          result.value.properties.FEATCODE
        });`
        query = query.split('"[[').join('[[').split(']]"').join(']]')
        yield* insertQuery(query)
      }
    }
  })
openShapeFile(`shapefile.shp`).then((source) => {
  const s = shapeFileGenerator(source)
})))

现在这个有效!它读取所有数据! 但是,我讨厌无限循环,我从不直接调用.next。我该如何重做呢?用发电机做这样的事情的惯用方法是什么?看来我能够用s.next()编写一个合适的生成器,从而产生source.read()

2 个答案:

答案 0 :(得分:0)

我会写

async function readFileToDB(filename) {
    const source = await openShapeFile(filename);
    for (let {value, done} = await source.read(); !done; {value, done} = await source.read()) {
        const query = `INSERT INTO geodata(geometry, id, featcode) VALUES(ST_GeomFromGeoJSON('${
            JSON.stringify(value.geometry)
        }'), '${
            value.properties.ID
        }', ${
            value.properties.FEATCODE
        });`
        const result = await pool.query(query);
    }
    console.log(dirCount);
}
readFileToDB(`shapefile.shp`).catch(err => console.error('Problem here', err));

艰难我认为递归解决方案没有任何问题。

  

似乎我应该能够使用s.next()编写一个合适的生成器,从而产生source.read()

不,发电机是同步的。您可能希望查看async iteration proposal

答案 1 :(得分:-1)

您可以将逻辑编码为同步,并通过顺序执行器nsynjs执行。以下是在this file上测试的略微修改的工作示例:

main.js:

var nsynjs = require('nsynjs');
var shapefile = require('shapefile');

function synchrobousCode(shapefile /*, pool */) {
    var source = shapefile.open('UScounties.shp').data;
    var result = source.read().data;
    while(result && !result.done) {
        var query = "INSERT INTO geodata(geometry, id, featcode) VALUES('" +
            JSON.stringify(Object.assign({}, result.value.geometry, {coordinates: result.value.geometry.coordinates.map(JSON.stringify)})) +
            "'), '" +
            result.value.properties.ID +
            "'," +
            result.value.properties.FEATCODE +
            "')";
        console.log(query.length);
        // uncomment line below to sequentially insert to the DB
        // var queryRes = pool.query(query).data;
        result = source.read().data;
    }
}

nsynjs.run(synchrobousCode,null,shapefile /*, pool */ ,function () {
    console.log('all done');
})

Nsynjs会自动检测某些函数调用是否返回promise。如果是,它将等待承诺解析,将结果放到data属性,然后继续下一个表达式。