0

I made a transaction function that simplifies this action for me like this (it working):

export async function transaction(queriesRaw) {
  let allResults = []

  const client = await pool.connect()

  try {
    await client.query('BEGIN')
    var queries = queriesRaw.map(q => {
      return client.query(q[0], q[1])
    })

    for await (const oneResult of queries) {
      allResults.push(oneResult)
    }
    await client.query('COMMIT')
  } catch (err) {
    await client.query('ROLLBACK')
  } finally {
    client.release()
    return allResults
  }
}

And do transactions like this:

let results = await transaction([
            ['UPDATE readers SET cookies=5 WHERE id=$1;', [1]],
            ['INSERT INTO rewards (id) VALUES ($1);', [3]]
          ])

Transaction should do queries one at a time in array index sequence (so rollback to previos values will work correctly) and return in the same order (sometimes i need return values from some queries)

As i understand it starts already in map map function. In for await i just wait for results of it and second query may complete faster that previos.

So how can i fix this?

P.S. Maybe something like new Promise() instead map is the rigth way?

ZiiMakc
  • 31,187
  • 24
  • 65
  • 105

2 Answers2

2

Change this:

var queries = queriesRaw.map(q => {
  return client.query(q[0], q[1])
})

for await (const oneResult of queries) {
  allResults.push(oneResult)
}

To:

for(const q of rawQueries) {
   let result = await client.query(q[0], q[1]);
   allResults.push(result);
});
Bibberty
  • 4,670
  • 2
  • 8
  • 23
1

If i got you correctly, Just use a for loop with proper await, instead of a callback style loop.

So you can wait with the function to return unil everything is chronologically executed, With some thinking, you can easily add aa revoke() function or something..


...
export async function transaction(queriesRaw) {
  let allResults = []

  const client = await pool.connect()

  try {
    await client.query('BEGIN')

    for(var i = 0; i < queriesRaw.length;i++) {
        var res = await client.query(queriesRaw[i][0], queriesRaw[i][1])
        allResults.push(res)
    }

    await client.query('COMMIT')
  } catch (err) {
    await client.query('ROLLBACK') 

    // do you maybe wanna errors to results to?
    // allResults.push(err)
  } finally {
    client.release()
    return allResults
  }
}

Info,

Have a look at for example async module, or something similar. So you will not have to think about things like this.

ZiiMakc
  • 31,187
  • 24
  • 65
  • 105
Silvan Bregy
  • 2,544
  • 1
  • 8
  • 21
  • 1
    I am, and just try it.. For explanation, You cannot use `await` within `forEach` because you pass a new function to it. So for having `await` in `forEach` you do this: `[fetch()].forEach(async res => console.log(await res))`. But then you'll have a problem because your code does not wait for the `forEach` to finish it's iterations. Read this: https://codeburst.io/javascript-async-await-with-foreach-b6ba62bbf404 – Silvan Bregy May 21 '19 at 20:23
  • 1
    thanks, for some reason this simple solution didn't get in my head as i was trying to make for await work :/ – ZiiMakc May 21 '19 at 20:26
  • 1
    That's why you should talk to other coders about your problems^^ – Silvan Bregy May 21 '19 at 20:27
  • It's solves my problem, but still question remains, how to make array of promises for new for await loop :) – ZiiMakc May 21 '19 at 20:28
  • I don't know either. I think you would have to use a generator: https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Statements/function* , And here's an example of await for loop :: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for-await...of – Silvan Bregy May 22 '19 at 08:44
  • The link pointing on `/function` should be `/function*`, with `*` – Silvan Bregy May 22 '19 at 08:46