How to set up multiple firebase .on() listeners and trigger function after all have resolved

Viewed 73

I have multiple on() listeners set up on different nodes to get data required for generating some tables of data. After a listener updates, it regenerates all the tables on the website. The problem is that on the first webpage load, it has to regenerate the tables once for each .on() listener, this is excessive. I tried making each listener into a promise so that I could run gen_tables() only after they all resolve and it worked great, except that the listeners seem to have been called off and no longer listen. How can I have the gen_tables() run only once on first load, but then still have my multiple listeners continue listening?

Here is my code that works, but has to run gen_tables() 6 times

//LISTENER - CURRENT_WEEK_DATA

db.ref(`weeks/${currentweek}`).on("value", (snap) => {
  currentWeekData = snap.val();
  console.log("DOWNLOADING currentWeekData From Firebase: ", currentWeekData);
  gen_tables();
});

//LISTENER - ORDERS_PROCESSING
db.ref("orders_processing").on("value", (snap) => {
  orders_processing = snap.val();
  console.log(
    "DOWNLOADING orders_processing From Firebase: ",
    orders_processing
  );
  gen_tables();
});

//LISTENER - ORDERS_META
db.ref("orders_meta").on("value", (snap) => {
  orders_meta = snap.val();
  console.log("DOWNLOADING orders_meta From Firebase");
  gen_tables();
});

//LISTENER - ORDERS NEXT WEEK Do i neeed this?
db.ref("orders_next_week").on("value", (snap) => {
  orders_next_week = snap.val();
  console.log("orders_next_week DOWNLOADED From Firebase: ", orders_next_week);
  gen_tables();
});

//LISTENER - PRODUCTS
db.ref("products").on("value", (snap) => {
  console.log("DOWNLOADING PRODUCTS");
  products = snap.val();
  gen_tables();
});

//LISTENER -PRODUCTS_META
db.ref("products_meta").on("value", (snap) => {
  console.log("DOWNLOADING PRODUCTS_META");
  products_meta = snap.val();
  gen_tables();
});

and here is the code with the Promise.all method to wait until all .on() have resolved to run gen_tables() only once, but doesn't continue listening after first load:

const listener = (ref) => {
  return new Promise((resolve, reject) => {
    const onError = (error) => reject(error);
    const onData = (snap) => resolve(snap.val());

    db.ref(ref).on("value", onData, onError);
  });
};

let listeners = [
  listener("orders_meta"),
  listener("orders_next_week"),
  listener("orders_processing"),
  listener("products"),
  listener("products_meta"),
  listener(`weeks/${currentweek}`),
];

Promise.all(listeners).then(function (values) {
  console.log(values);
  orders_meta = values[0];
  orders_next_week = values[1];
  orders_processing = values[2];
  products = values[3];
  products_meta = values[4];
  currentWeekData = values[5];
  gen_tables();
});
1 Answers

You should rethink your architecture. Since you're not passing any values to gen_tables() I'm assuming it updates all tables, is that correct? In that case why not set up a Cloud Function that's triggered by Cloud Scheduler every n minutes to execute gen_tables()?

If you want to only call gen_tables() if an update has occurred, use your .on() listeners to set a value in your database to 1 when a write happens and when your Cloud Function executes it only calls gen_tables() if that value is 1.

If you want more nuanced updates, use your .on() listeners to update a queue with the table name to update and then have a Cloud Scheduler-triggered Cloud Function check every n minutes for updates and process those.

Related