2017-07-19 4 views
1

EDIT: Ich bin mit Knoten V8.0.0Async/Await Node-Postgres Abfragen in foreach-Schleife

ich lerne gerade erst begonnen, wie SQL-Datenbanken mit Knoten-Postgres zugreifen zu können, und ich habe ein bisschen Probleme beim Zugriff auf mehrere Datenbanken, um die Daten in einem arbeitsfähigen Format zu sammeln, insbesondere bei der Ausführung mehrerer Abfragen innerhalb von forEach-Schleifen. Nach ein paar Versuchen, ich versuche async/await, aber ich bekomme die folgende Fehlermeldung:

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

Wenn ich einen Pool oder rufen .query nacheinander versucht, mit, würde ich etwas entlang der Linien von

bekommen Hier
1 
[] 
could not connect to postgres Error: Connection terminated 

ist eine gekürzte Version meines Code:

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

Antwort

1

Das Problem wird dadurch verursacht, indem Sie Ihre forEach Rückruf nicht async sein:

snaptimes.forEach(function(snaptime){ 

sollte sein:

snaptimes.forEach(async function (snaptime) { 

für die await überhaupt erkennbar zu sein.

Beachten Sie, dass eine async Funktion kehrt sofort zurück und es gibt ein Versprechen, das durch return Aussagen der async Funktion (oder abgelehnt mit abgefangene Ausnahmen die innerhalb der async Funktion) schließlich gelöst wird.

aber auch sicherstellen, dass Ihre Node-Version unterstützt async/await:

  • Da Knoten 7.6 kann ohne --harmony Flagge verwendet werden.
  • In Knoten 7.x vor 7.6 müssen Sie das Flag --harmony verwenden.
  • Es war nicht in Knoten vor 7.0 verfügbar.

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

Beachten Sie auch, dass Sie awaitnur innerhalb von Funktionen deklariert mit dem async Schlüsselwort verwenden können.Wenn Sie es in der obersten Ebene des Skripts oder Modul verwenden möchten, dann müssen Sie es in einer unmittelbar aufgerufen Funktionsausdruck wickeln:

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

Beispiel:

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

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

druckt:

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

aber:

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

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

druckt:

$ node t2.js 
x 

nach 0,5s Verzögerung, wie erwartet.

Bei Versionen von Knoten, die nicht async/await das erste (falsche) Beispiel unterstützen gedruckt wird:

drucken
$ ~/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 

und das zweite (korrekt) Beispiel einen anderen Fehler:

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

Es ist nützlich zu wissen, weil Knotenversionen, die async/await nicht unterstützen, Ihnen keinen sinnvollen Fehler wie "async/erwarten nicht unterstützt" oder so etwas geben, leider.

+0

Async vor jeder Funktionsaufforderung zu setzen, klärte das Problem sehr schön auf. Wenn ich jedoch versuche, dem snaptime-Array, in dem der erste forEach-Aufruf aufgerufen wird, weitere snaptimes hinzuzufügen, erhalte ich entweder 'Fehler: Verbindung wurde unerwartet beendet' oder 'Fehler: Lese ECONNRESET'. Wenn diese Anweisungen nicht mehr gedruckt werden, treten diese Fehler sofort bei Abfrage1 auf. Das Verschieben von client.end() nach der forEach-Schleife bewirkt nur, dass der Code nicht ausgeführt wird –

+0

@HansyPiou Wenn Sie async/await verwenden, würde ich stattdessen 'for (let snaptime of snaptimes) {...}' anstelle von empfehlen 'snaptimes.forEach (function (snaptime) {...});' weil Sie auf diese Weise keine anderen Funktionen aufrufen und es einfacher ist, die Iterationsschritte zu synchronisieren. – rsp

+0

Die ganzen Dinge funktionieren jetzt super! Die Verwendung von for() - Schleifen bedeutete, dass ich sie alle in eine größere (asynchrone) Funktion einbinden musste, was aber letztlich Probleme mit Node-Postgres beseitigte, da ich den Client vor dem Sammeln der Daten verbinden und schließen konnte, wenn ich fertig war. Danke für all deine Hilfe! –

0

Vergewissern Sie sich, dass Sie async Block außen wie verwenden sollten:

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

Und es standardmäßig nach unterstützt Knoten 7.6.0. Vor 7.6.0 sollten Sie die Option --harmony verwenden, um dafür zu arbeiten.

node -v zuerst, um Ihre Version zu überprüfen.

0

Zunächst einmal wissen Sie nicht genug über async-erwarten Sie gerade noch. Mach dir keine Sorgen, es ist eigentlich ganz einfach; aber Sie müssen die Dokumentation lesen, um dieses Zeug verwenden zu können.

Mehr zu dem Punkt, das Problem mit Ihrem Code ist, dass Sie nur await innerhalb async Funktionen können; Sie tun das außerhalb von jede Funktion.

Vor allem hier ist die Lösung, die am nächsten zu dem Code Sie schreibt:

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 gibt ein Versprechen, und ich verwende thenrun anrufen, sobald es gelöst ist. Wenn der Teil ist vorbei, client.end() kann sicher aufgerufen werden.

run ist eine async Funktion, daher kann await verwendet werden, um den Code lesbarer zu machen. Das gleiche gilt für processSnaptime.

Natürlich kann ich nicht wirklich laufen Ihren Code, so kann ich nur hoffen, dass ich keine Fehler gemacht habe.