在ForEach循环中异步/等待Node-Postgres查询

时间:2017-07-19 15:22:59

标签: node.js postgresql asynchronous foreach node-postgres

编辑:我使用的是节点v8.0.0

我刚刚开始学习如何使用node-postgres访问SQL数据库,并且我在访问多个数据库以便以可工作的格式收集数据方面遇到一些麻烦,特别是在forEach循环中执行多个查询时。经过几次尝试后,我尝试了async / await,但是我收到了以下错误:

await client.connect()
  ^^^^^^
SyntaxError: Unexpected identifier

当我尝试使用游泳池或按顺序调用.query时,我会得到类似

的内容
1
[]
could not connect to postgres Error: Connection terminated

以下是我的代码的缩写版本:

const { Client } = require('pg');
const moment = require('moment');
const _ = require('lodash');
const turf = require('@turf/turf');

const connString = // connection string
var collected = []
const CID = 300
const snaptimes = // array of times
var counter=0;
const client = new Client(connString);

function createArray(i,j) {
     // return array of i arrays of length j
}

await client.connect()

snaptimes.forEach(function(snaptime){
  var info = {}; // an object of objects
  // get information at given snaptime from database 1
  const query1 = // parametrized query selecting two columns from database 1
  const result1 = await client.query(query1, [CID,snaptime]);
  var x = result1.rows;
  for (var i = 0; i < x.length; i++) {
     // store data from database 1 into info
     // each row is an object with two fields
  }

  // line up subjects on the hole
  const query2 = // parametrized query grabbing JSON string from database 2
  const result2 = await client.query(query2, [CID,snaptime]);
  const raw = result2.rows[0].JSON_col;
  const line = createArray(19,0); // an array of 19 empty arrays
  for (var i = 0; i < raw.length; i++) {
     // parse JSON object and record data into line 
  }

  // begin to collect data
  var n = 0;
  var g = 0;
  // walk down the line
  for (var i = 18; i > 0; i--) {
    // if no subjects are found at spot i, do nothing, except maybe update g
    if ((line[i] === undefined || line[i].length == 0) && g == 0){
      g = i;
    } else if (line[i] !== undefined && line[i].length != 0) {
      // collect data for each subject if subjects are found
      line[i].forEach(function(subject){
        const query 3 = // parametrized query grabbing data for each subject 
        const result3 = await client.query(query3,[CID,subject,snaptime]);
        x = result3.rows;
        const y = moment(x[0].end_time).diff(moment(snaptime),'minutes');
        var yhat = 0;
        // the summation over info depends on g
        if (g===0){
          for (var j = i; j <= 18; j++){
            yhat = moment.duration(info[j].field1).add(yhat,'m').asMinutes();
          }
        } else {
          for (var j = i; j <= 18; j++){
            if (i<j && j<g+1) {
              yhat = moment.duration(info[j].field2).add(yhat,'m').asMinutes();
            } else {
              yhat = moment.duration(info[j].field1).add(yhat,'m').asMinutes();
            }
          }
        }
        collected.push([y,yhat,n,i]);
      });
    }
    n+=line[i].length;
    g=0;
  }
  // really rough work-around I once used for printing results after a forEach of queries
  counter++;
  if (counter===snaptimes.length){
    console.log(counter);
    console.log(collected);
    client.end(); 
  }
});

3 个答案:

答案 0 :(得分:1)

问题是由于您的forEach回调不是async

snaptimes.forEach(function(snaptime){

应该是:

snaptimes.forEach(async function (snaptime) {

await可以识别。

请记住,async函数会立即返回,并返回一个承诺,该承诺最终由return函数的async语句解析(或者在async函数中引发的未捕获异常被拒绝{1}}功能)。

但请确保您的Node版支持async / await

  • 从Node 7.6起,它可以与--harmony标志一起使用。
  • 在7.6之前的Node 7.x中,您必须使用--harmony标志。
  • 在7.0之前的节点中不可用。

请参阅:http://node.green/#ES2017-features-async-functions

另请注意,您可以在使用await关键字声明的函数内使用async 。如果要在脚本或模块的顶层使用它,则需要将其包装在一个立即调用的函数表达式中:

// cannot use await here
(async () => {
  // can use await here
})();
// cannot use await here

示例:

const f = () => new Promise(r => setTimeout(() => r('x'), 500));

let x = await f();
console.log(x);

打印:

$ node t1.js 
/home/rsp/node/test/prom-async/t1.js:3
let x = await f();
              ^
SyntaxError: Unexpected identifier

但是这个:

const f = () => new Promise(r => setTimeout(() => r('x'), 500));

(async () => {
  let x = await f();
  console.log(x);
})();

打印:

$ node t2.js 
x
延迟0.5秒后,正如预期的那样。

在不支持async / await的节点版本上,将打印第一个(不正确的)示例:

$ ~/opt/node-v6.7.0/bin/node t1.js 
/home/rsp/node/test/prom-async/t1.js:3
let x = await f();
              ^
SyntaxError: Unexpected identifier

和第二个(正确的)示例将打印一个不同的错误:

$ ~/opt/node-v6.7.0/bin/node t2.js 
/home/rsp/node/test/prom-async/t2.js:3
(async () => {
       ^
SyntaxError: Unexpected token (

了解它非常有用,因为不支持async / await的节点版本不会给您带来有意义的错误,例如&#34; async / await不支持&# 34;或者类似的东西,不幸的是。

答案 1 :(得分:0)

确保您应该使用async外部阻止,如:

async function() {
  return await Promise.resolve('')
}

在节点7.6.0之后默认支持它。在7.6.0之前,您应该使用--harmony选项来为它工作。

node -v首先检查您的版本。

答案 2 :(得分:0)

首先,你还不太了解async-await。别担心,实际上很容易;但是你需要阅读文档才能使用那些东西。

更重要的是,您的代码存在的问题是awaitasync函数内部只能const { Client } = require('pg'); const moment = require('moment'); const _ = require('lodash'); const turf = require('@turf/turf'); const connString = // connection string var collected = [] const CID = 300 const snaptimes = // array of times var counter=0; const client = new Client(connString); function createArray(i,j) { // return array of i arrays of length j } async function processSnaptime (snaptime) { var info = {}; // an object of objects // get information at given snaptime from database 1 const query1 = // parametrized query selecting two columns from database 1 const result1 = await client.query(query1, [CID,snaptime]); var x = result1.rows; for (var i = 0; i < x.length; i++) { // store data from database 1 into info // each row is an object with two fields } // line up subjects on the hole const query2 = // parametrized query grabbing JSON string from database 2 const result2 = await client.query(query2, [CID,snaptime]); const raw = result2.rows[0].JSON_col; const line = createArray(19,0); // an array of 19 empty arrays for (var i = 0; i < raw.length; i++) { // parse JSON object and record data into line } // begin to collect data var n = 0; var g = 0; // walk down the line for (var i = 18; i > 0; i--) { // if no subjects are found at spot i, do nothing, except maybe update g if ((line[i] === undefined || line[i].length == 0) && g == 0){ g = i; } else if (line[i] !== undefined && line[i].length != 0) { // collect data for each subject if subjects are found line[i].forEach(function(subject){ const query 3 = // parametrized query grabbing data for each subject const result3 = await client.query(query3,[CID,subject,snaptime]); x = result3.rows; const y = moment(x[0].end_time).diff(moment(snaptime),'minutes'); var yhat = 0; // the summation over info depends on g if (g===0){ for (var j = i; j <= 18; j++){ yhat = moment.duration(info[j].field1).add(yhat,'m').asMinutes(); } } else { for (var j = i; j <= 18; j++){ if (i<j && j<g+1) { yhat = moment.duration(info[j].field2).add(yhat,'m').asMinutes(); } else { yhat = moment.duration(info[j].field1).add(yhat,'m').asMinutes(); } } } collected.push([y,yhat,n,i]); }); } n+=line[i].length; g=0; } // really rough work-around I once used for printing results after a forEach of queries counter++; if (counter===snaptimes.length){ console.log(counter); console.log(collected); } } async function run () { for (let snaptime of snaptimes) { await processSnaptime(snaptime); } } /* to run all of them concurrently: function run () { let procs = []; for (let snaptime of snaptimes) { procs.push(processSnaptime(snaptime)); } return Promise.all(procs); } */ client.connect().then(run).then(() => client.end()); ;你在任何功能之外做这件事。

首先,这里的解决方案最接近您编写的代码:

client.connect

then会返回一个承诺,我会使用runclient.end()解决后致电run。当 部分结束时,可以安全地调用async

awaitprocessSnaptime函数,因此可以使用app.config['MYSQL_DATABASE_USER'] = 'root' app.config['MYSQL_DATABASE_PASSWORD'] = '' app.config['MYSQL_DATABASE_DB'] = 'UserList' app.config['MYSQL_DATABASE_HOST'] = '172.17.0.3' 使代码更具可读性。 env | grep MYSQL MYSQL_PORT_5123_TCP_ADDR=172.17.0.3 MYSQL_ENV_MYSQL_ROOT_PASSWORD=test MYSQL_PORT_5123_TCP=tcp://172.17.0.3:5123 MYSQL_PORT_5123_TCP_PROTO=tcp MYSQL_ENV_GOSU_VERSION=1.7 MYSQL_PORT_3306_TCP_PORT=3306 MYSQL_PORT_3306_TCP=tcp://172.17.0.3:3306 MYSQL_PORT_5123_TCP_PORT=5123 MYSQL_ENV_MYSQL_VERSION=5.7.18-1debian8 MYSQL_NAME=/site-metrics/mysql MYSQL_PORT_3306_TCP_PROTO=tcp MYSQL_PORT_3306_TCP_ADDR=172.17.0.3 MYSQL_ENV_MYSQL_MAJOR=5.7 MYSQL_PORT=tcp://172.17.0.3:3306 也是如此。

当然,我无法运行您的代码,所以我只能希望我没有犯任何错误。