0

Resolved with answers, thanks everyone

EDIT : I want my updateInventory function to return an object that have a property for each promise. I can not know neither which ones will be used nor the order The problem is that t.batch and Promise.all return an index array so I can not associate a promise to its component to output an object like I want.

Inspired by this post I did this:

The then in getStatement is executed first to format the resulting data of each promise : an object with name and data properties. Then t.batch is resolved with all of these objects. The result is still an index array but each object contains the name and the data. So I can create my final object. Maybe there is a better way to do it ?

An example below : My input :

{
    device: {
        deviceId: 1,
        name: "myName",
        istate: "updated"
    },
    system: {
        systemId: 50,
        domain: 'foo.fr',
        istate: "updated"
    },
    ifaces: [
        {
            interfaceId: 75,
            name: "Iface Update 1",
            istate: "updated"
        },
        {
            name: "New Interface 1",
            istate: "added"
        }
    ]
}

"standard" Output with only t.batch() or `Promise.all() : an index array

[
    0: { deviceId: 1 },
    1: { systemId: 50 },
    2: [
        { interfaceId: 75 },
        { interfaceId: 76 }
    ]
]

My custom t.batch output :

[
    0: { name: "device", data: { deviceId: 1 }}
    1: { name: "system", data: { systemId: 50 }}
    2: { name: "ifaces", data: [{ interfaceId: 75 },{ interfaceId: 76 }] }
]

And then I can build my final object (values.forEach below) :

{
  "device": { deviceId: 1 },
  "system": { systemId: 50 },
  ifaces: [
        { interfaceId: 75 },
        { interfaceId: 76 }
  ]
}

The code (simplified):

function updateInventory(params) {
  const { /* hidden */, ...components } = params.data; // simplified
  
  return Database.tx('Inventory-Update', t => { // postgres transaction with pg-promise library
    const statements = []; // promise array

    for(let [prop, component] of Object.entries(components)) {
      statements.push(getStatement(t, prop, component));
    }

    return t.batch(statements);
  }).then(results => {
    let result = {};
    results.forEach(res => {
      result[res.name] = res.data;
    });
    return result;
  });
}

function getStatement(t, prop, component) {
  const repo = getReposName(prop);
  const state = component.istate;

  let statement;

  switch(state) {
    case "updated":
      statement = t[repo].update(component);
      break;
    case "removed":
      statement = t[repo].delete(component);
      break;
    default:
      statement = t[repo].insert(component);
      break;
  }
  
  return statement.then(res => {
    return {name: prop, data: res };
  });
}
Community
  • 1
  • 1
Firlfire
  • 413
  • 1
  • 6
  • 14
  • It's very difficult when you speak in terms of line numbers. Please provide an minimal example and whats not working. – Vishnudev Krishnadas Sep 02 '19 at 14:05
  • @Vishnudev, that last comment does not make sense. calling `then` does not make code wait. Also, it does not influence the value of `statement`. – trincot Sep 02 '19 at 14:13
  • A few things look wrong with the code, starting with `t[repo]`, which causes loss of the calling context. Should pass in just `t`, and then do `t.repo` inside instead. And then this attempt at resolving the promises twice - one time inside the transaction function, when calling `t.batch`, and another time inside `getStatement`. – vitaly-t Sep 02 '19 at 14:24
  • If `statement` is `Promise` in your `getStatement` (which seems to be the case) then the output (array of promises) is correct – 1565986223 Sep 02 '19 at 14:26

3 Answers3

1

It is based on how Promises works. then means that after promise is resolved, it will run next part. However you can have multiple then added to single promise and they do not affect each other.

You can see in this example that the Promise all is resolved before the then statements (because it just wait for statement to be finished, not for the waitABit part).

function getStatement() {
  const statement = waitABit();

  statement.then(res => {
    let foo={name: Math.random(), data: [Math.random()] };
    console.log(foo);
    return foo;
  });
  return statement;
}

async function waitABit() {
  return new Promise(resolve => setTimeout(resolve,1000));
}

async function callAll() {
  statements = [];
  for (let i=0; i < 3; i++){
    statements.push(getStatement());
  }
  console.log(await Promise.all(statements));
}

callAll();

This part is easy to fix:

    function getStatement() {
      let statement = waitABit();

      const afterStatement = statement.then(res => {
        let foo={name: Math.random(), data: [Math.random()] };
        console.log(foo);
        return foo;
      });
      return afterStatement;
    }

    async function waitABit() {
      return new Promise(resolve => setTimeout(resolve,1000));
    }

    async function callAll() {
      statements = [];
      for (let i=0; i < 3; i++){
        statements.push(getStatement());
      }
      console.log(await Promise.all(statements));
    }

    callAll();

But in your case its probably also not an option, because after that you will put your promises that will be resolved with nam and data into t.batch(statements); which will probably fail.

You need to approach this in different way - i.e. getStatement will return data structure with these updates in DB and with the data you need, then you wait till the data updates and then you continue further.

libik
  • 22,239
  • 9
  • 44
  • 87
  • Note that he cannot use `Promise.all` in his context, because he needs to settle all promises, which is what `t.batch` does there. – vitaly-t Sep 02 '19 at 14:25
1

Your expectation is wrong and the output you get is correct (array of promises)

Simple example:

function getPromise() {
  return new Promise((resolve, reject) => {
    setTimeout(() => resolve("data"), 200)
  })
}

function something() {
  // codes ...
  let res = getPromise()
  res.then(d => {
    // you're expecting this to be returned, but remember this runs in Future
    // this part of execution happens after res is returned in the last line
    console.log('this is executed after return')
    return d;
  })
  return res; // <-- this is what is actually returned
}

console.log(something())

Note: for some reason I can see only {}, not Promise { <state>: "pending" } while running this snippet in Firefox, if that is the case open your browser to confirm output is Promise

Solution?

Perhaps?:

// get rid of statement.then...
return { name, data: statement };
1565986223
  • 6,420
  • 2
  • 20
  • 33
1

One problem is that .then() returns a new promise so you need to change this:

  statement.then(res => {
    let foo={name: name, data: res };
    console.log(foo);
    return foo;
  });
  return statement;

to this:

  return statement.then(res => {
    let foo={name: name, data: res };
    console.log(foo);
    return foo;
  });

You need to be returning the new promise that .then() returns because that's the promise that will have foo as the resolved value.

jfriend00
  • 683,504
  • 96
  • 985
  • 979