2017-07-19 175 views
1

编辑:我使用节点V8.0.0异步/等待节点的Postgres查询中foreach循环

我刚开始学习如何访问SQL数据库与节点的Postgres,和我有一点点无法访问多个数据库以可工作的格式收集数据,尤其是在forEach循环内执行多个查询时。试了几次后,我想异步/ 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(); 
    } 
}); 

回答

1

问题是由您的forEach回调不是async引起的:

snaptimes.forEach(function(snaptime){ 

应该是:

snaptimes.forEach(async function (snaptime) { 

await可识别的。

请记住,一个async函数立即返回,并返回一个承诺,得到最终由async功能return语句解析(或与async函数内引发捕获的异常拒绝)。

但也请确保您的节点版本支持async/await

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

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

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

// 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的节点版本不会给你一个像“async/await not supported”之类的有意义的错误,或者类似的东西。

+0

在任何函数调用await之前进行异步解决问题非常好。但是,当我尝试向第一个forEach被调用的snaptime数组添加更多的快照时,我要么'错误:连接意外终止'或'错误:读ECONNRESET'。从打印语句开始,这些错误立即发生在query1上。将client.end()移动到forEach循环之后只会导致代码不运行 –

+0

@HansyPiou当你使用async/await时,我建议使用'for(让snaptime快照时间){...}'而不是'snaptimes.forEach(function(snaptime){...});'因为这样你不会调用其他函数,并且更容易同步迭代步骤。 – rsp

+0

现在整个事情都很棒!使用for()循环意味着我不得不将它们全部包装到一个更大的(异步)函数中,但是最终清除了node-postgres的问题,因为我可以在收集数据之前连接客户端,并在完成时关闭它。谢谢你的帮助! –

0

确保您应该使用外async块,如:

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

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

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

0

首先,你还不够了解异步等待。别担心,这其实很简单;但你需要阅读文档才能使用这些东西。

更重要的是,您的代码的问题是,您只能await里面async函数;你在做以外的任何函数。

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

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调用run问题解决后。当部分结束时,可以安全地调用client.end()

runasync函数,因此它可以使用await使代码更具可读性。 processSnaptime也是如此。

当然,我实际上不能运行你的代码,所以我只能希望我没有犯任何错误。