node.jspostgresqlpromisepg-promisersvp-promise

Resolve array of promises node.js


I am new to promises, I am trying to use RSVP promises in Node.js with PostgreSQL and I am doing it wrong, most probably. Any suggestions on how to fix that or how to improve the code are appreciated.

What I try to achieve is: after receiving data - process the data to create SQL update queries and when they are ready - execute them. Data here is array of user ids.

What does not work: I get array of array of promises that doesn't resolve, I tried to resolve the array like so:

var promise4all = RSVP.all(
  updateQueries.map((innerPromiseArray) => {
    return RSVP.all(innerPromiseArray);
  })
);

promise4all.then((promiseGroupResult) => {
     // doesn't get here
});

But it didn't work also.

The code:

1) The function 'update' that receives data and calls function 'promiseQuery' to process the data:

const RSVP = require('rsvp');

let db;

const update = (data) => {      
  let users = {
    items: data.user, // data to be updated in db - array of user ids
    item_type: 1,
    id: data.department
  }

  let updateQueries = [];

  // adding query promises to updateQueries
  updateQueries.push(promiseQuery(users.id, users.item_type, users.items));

  RSVP.all(updateQueries).then((results) => {

    /* here 'results' looks like that: 
       [ [ { query: 'INSERT INTO link_to_department (item_type, department, item) VALUES ($item_type, $department, $item)',
             values: [Object] },
          { query: 'DELETE FROM link_to_department WHERE department = $(department) AND item_type = $(item_type) AND item=$(item)',
             values: [Object] } ] ] 

    db update below fails with '[Empty or undefined query.]'*/

    db.tx((trx) => {
        let sqlUpdates = [];

        results.forEach((query) => {
            sqlUpdates.push(trx.none(query.query, query.values))
        })

        return trx.batch(sqlUpdates);
    }).then(() => {
        res.sendStatus(204);
    }).catch((err) => {
        console.log('error', err.message);
        // handle errors
    });
  });
};

2) The function 'promiseQuery' processes data (it compares received data and data in db to update db with the new data):

const promiseQuery = (department_id, item_type, items) => {
    return new RSVP.Promise((resolve, reject) => {
        db.query('SELECT item FROM link_to_department WHERE department=' + department_id + ' AND item_type=' + item_type)
          .then((db_items) => {
            let promises = [];

            let itemsToBeRemoved = [];
            let itemsToBeAdded = [];

            /* here we have array of user ids we received: 'items' 
               and array of user ids from db: 'db_items' */

            // populating 'itemsToBeAdded' and 'itemsToBeRemoved' with user ids that need to added or removed:
            populateUpdateArray(items, db_items, itemsToBeAdded);
            populateUpdateArray(db_items, items, itemsToBeRemoved);

            let insert_query = 'INSERT INTO link_to_department (item_type, department, item) VALUES ($item_type, $department, $item)'
            let delete_query = 'DELETE FROM link_to_department WHERE department = $(department) AND item_type = $(item_type) AND item=$(item)';

            // creating update sql queries
            populateUpdateQuery(insert_query, itemsToBeAdded, department_id, item_type, promises);
            populateUpdateQuery(delete_query, itemsToBeRemoved, department_id, item_type, promises);

            RSVP.all(promises).then((results) => {
               /* here 'results' looks like this:
                  [ { query: 'INSERT INTO link_to_department (item_type, department, item) VALUES ($item_type, $department, $item)',
                      values: { item_type: 19, department: 1, item: '1' } },  
                    { query: 'DELETE FROM link_to_department WHERE department = $(department) AND item_type = $(item_type) AND item=$(item)',
                      values: { item_type: 19, department: 1, item: 1 } }] */

                return resolve(results);
            });

        }).catch(() => {
           reject();
    })
  });
};

3) That function 'populateUpdateArray' populates array of user ids that need to be updated (basically, received user ids should replace ids in the db - for that we check what ids we received are not in db and what ids in db are not in the received ids):

const populateUpdateArray = (array_0, array_1, updateArray) => {
   array_0.forEach((item) => {
      if (array_1.indexOf(item) === -1) {
        updateArray.push(item);
     }
  });
};

4) That function 'populateUpdateQuery' returns sql update queries:

const populateUpdateQuery = (query, id_array, department_id, item_type, promises) => {
   return new RSVP.Promise((resolve, reject) => {
    id_array.forEach((item) => {
        let values = {
            item_type: item_type,
            department: department_id,
            item: item
        };

        promises.push({query, values});
    });

    resolve(promises);      
  });
};

Thank you!

EDIT: I changed the code to have only one db connection and I simplified the code a little. I do not get any errors, but queries are not executed, still. I think I am missing something basic here:

const update = (data) => {
    let users = {
        items: data.user,
        item_type: 1,
        id: data.department
    }

    db.tx((tx) => {
        let updateQueries = [];

        updateQueries.push(promiseQuery(department.id, users.item_type, users.items, tx));

        RSVP.all(updateQueries).then((results) => {
            // results is array of array, so i flatten it
            let sqlUpdates = results.reduce((a, b) => { return a.concat(b); }, []);

            /* sqlUpdates here:   
             [ Promise {
                 _bitField: 0,
                 _fulfillmentHandler0: undefined,
                 _rejectionHandler0: undefined,
                 _promise0: undefined,
                 _receiver0: undefined } ]
            */

            return tx.batch(sqlUpdates);
        });
   }).then(() => {
       res.sendStatus(204);
   }).catch((err) => {
       console.log('error', err.message);
   });
};

const promiseQuery = (department_id, item_type, items, tx) => {
   return new RSVP.Promise((resolve, reject) => {
     tx.query('SELECT item FROM belongs_to_departments WHERE department=' + department_id + ' AND item_type=' + item_type)
        .then((db_items)=> {
            let queries = [];               
            let itemsToBeAdded = [];
            let insert_query = 'INSERT INTO belongs_to_departments (item_type, department, item) VALUES ($(item_type), $(department), $(item))';

            populateUpdateArray(items, db_items, itemsToBeAdded);
            populateUpdateQuery(insert_query, itemsToBeAdded, department_id, item_type, queries, tx);

            resolve(queries);
        }).catch(() => {
            reject();
        });
   });
};

const populateUpdateArray = (array_0, array_1, updateArray) => {
  array_0.forEach((item) => {
     if (array_1.indexOf(item) === -1) {
        updateArray.push(item);
     }
  });
};

const populateUpdateQuery = (query, id_array, department_id, item_type, queries, tx) => {
   id_array.forEach((item) => {
        let values = {
            item_type: item_type,
            department: department_id,
            item: item
        };

        queries.push(tx.none(query, values));
   });
};

Solution

  • Thanks to Vitaly for the help. That worked for me:

    const update = data => {
        const users = {
            items: data.user,
            item_type: 1,
            id: data.department
        }
    
        db.tx(tx => {
            const updateQueries = [];
    
            updateQueries.push(promiseQuery(department.id, users.item_type, users.items, tx));
    
            RSVP.all(updateQueries).then(results => {
                // results is array of array, so i flatten it
                const sqlUpdates = results.reduce((a, b) => { return a.concat(b); }, []);                          
    
                return tx.batch(sqlUpdates);
            });
       }).then(() => {
           res.sendStatus(204);
       }).catch(err => {
           console.log('error', err.message);
       });
    };
    
    const promiseQuery = (department_id, item_type, items, tx) => {
       return new RSVP.Promise((resolve, reject) => {
         tx.query('SELECT item FROM belongs_to_departments WHERE department=' + department_id + ' AND item_type=' + item_type)
            .then(db_items => {
                const queries = [];               
                const itemsToBeAdded = [];
                const insert_query = 'INSERT INTO belongs_to_departments (item_type, department, item) VALUES ($(item_type), $(department), $(item))';
    
                populateUpdateArray(items, db_items, itemsToBeAdded);
                populateUpdateQuery(insert_query, itemsToBeAdded, department_id, item_type, queries, tx);
    
                resolve(queries);
            }).catch(() => {
                reject();
            });
       });
    };
    
    const populateUpdateArray = (array_0, array_1, updateArray) => {
      array_0.forEach((item) => {
         if (array_1.indexOf(item) === -1) {
            updateArray.push(item);
         }
      });
    };
    
    const populateUpdateQuery = (query, id_array, department_id, item_type, queries, tx) => {
       id_array.forEach(item => {
            const values = {
                item_type: item_type,
                department: department_id,
                item: item
            };
    
            queries.push(tx.none(query, values));
       });
    };