JavaScript с проблемой синхронизации Firebase - PullRequest
0 голосов
/ 11 мая 2018

Когда я запускаю этот код, я хотел добавить массив final в коллекцию firestore -> sendGrid , но он всегда пуст, хотя когда я его печатаю, он на самом деле имеет значения.

Я полагаю, что это из-за проблемы синхронизации, я всегда получаю [] -> value is just evaluated now (предупреждение), и когда я расширяю его, оно имеет значение.

function test() {

  let today = new Date();
  let addedDate = new Date(today.addDays(7));
  let final = [];
  let counter = 0;
  let adder = new Promise(function (resolve, reject) {
  db.collection("email").get()
    .then((querySnapshot) => {
      console.log(querySnapshot);
      if (querySnapshot.empty !== true) {
        querySnapshot.forEach((data) => {
          console.log(data.data());
          console.log(data.id);
          let db2 = db.collection("email").doc(data.id);
          let foodArr = [];
          if (data.data() !== null) {
            console.log(addedDate);
            if (addedDate >= userList[0].exxpiaryDate) {
              console.log("True");
            }
            db2.collection("list").where("expiaryDate", "<", addedDate.getTime()).get()
              .then((list) => {
                if (list.empty !== true) {
                  list.forEach((food) => {
                  if (food !== null) {
                    let temp = {
                      name: food.data().name,
                      time: food.data().expiaryDate,
                    };
                    foodArr.push(temp);
                    console.log(foodArr);
                  }
                })
              }
              if (foodArr.length !== 0) {
                let emailArr = {
                  email: data.data().email,
                  food: foodArr
                };
                console.log(emailArr);
                final[counter] = (emailArr);
                counter++;
                console.log(final[0]);
              }
            }).catch((err) => {
              console.log(err);
            });
          }
        });
      }
      console.log(final);
      resolve(final);
    }).catch((err) => {
      console.log(err);
    });
  });
  return adder;
}

async function add() {
  let add = await test();
  console.log(add);
  db.collection("sendGrid").add({
    response: add
  }).then((item) => {
    console.log(item);
  }).catch((err) => {
    console.log(err);
  });
}

1 Ответ

0 голосов
/ 11 мая 2018

Некоторые баллы: (Google, если не знаете, почему)

- prefer const
- return early
- clean code (eg. from console.log:s)
- cache fn calls
- functional programming is neat, look up Array.(map, filter, reduce, ...)
- destructuring is neat
- use arr[arr.length] = x; or arr.push(x), no need to manage your own counter
- short-circuit is sometimes neat (condition && expression; instead of if (condition) expression;)
- is queryResult.empty a thing? If it's a normal array, use !arr.length
- define variables in the inner most possible scope it's used in
- if having a promise in an async, make sure to return it
- prefer arrow functions

Я изменил код, чтобы следовать этим пунктам:

const test = ()=> {
  const today = new Date();
  const addedDate = new Date(today.addDays(7));

  return new Promise((resolve)=> {
    const final = [];
    const emailsQuery = db.collection("email")

    // an async/promise/then that's inside another promise, but not returned/awaited
    emailsQuery.get().then((querySnapshot) => {
      querySnapshot
        .map(data=> ({id: data.id, data: data.data()}))
        .filter(o=> o.data)
        .forEach(({id, data: {email}}) => {

        const db2 = db.collection("email").doc(id);
        const itemsQuery = db2.collection("list").where("expiaryDate", "<", addedDate.getTime())

        // another one!
        itemsQuery.get().then((items) => {
          const food = items.filter(o=> o).map(o=> o.data()).filter(o=> o)
            .forEach(({name, expiaryDate: time})=> ({name, time}))

          food.length && final.push({email, food})
        }).catch(console.error);
      });

      // resolving before the two async ones have finished!!
      resolve(final);
    }).catch(console.error);
  });
}

const add = async ()=> {
  let response = await test();
  return db.collection("sendGrid").add({response})
    .then((item) => console.log('item:', item))
    .catch(console.error)
}

Теперь мы видим, что есть проблема с асинхронным потоком («проблема синхронизации» в ваших словах). Я добавлю еще одну лучшую практику:

- use async/await when possible

Изменение с помощью этого делает его более понятным и решает проблему:

const test = async ()=> {
  const today = new Date();
  const addedDate = new Date(today.addDays(7));
  const emailsQuery = db.collection("email")
  const querySnapshot = await emailsQuery.get()
  const emailEntries = querySnapshot
    .map(data=> ({id: data.id, data: data.data()}))
    .filter(o=> o.data)

  // invoking an async fn -> promise; map returns the result of all invoked fns -> array of promises
  const promisedItems = emailEntries.map(async ({id, data: {email}}) => {
    const db2 = db.collection("email").doc(id);
    const itemsQuery = db2.collection("list").where("expiaryDate", "<", addedDate.getTime())

    const items = await itemsQuery.get()
    const food = items.filter(o=> o).map(o=> o.data()).filter(o=> o)
      .forEach(({name, expiaryDate: time})=> ({name, time}))

    return {email, food}
  });

  const items = await Promise.all(promisedItems)
  return items.filter(item=> item.food.length)
}

const add = async ()=> {
  let response = await test();
  return db.collection("sendGrid").add({response})
    .then((item) => console.log('item:', item))
    .catch(console.error)
}

Теперь поток ясен!

.


Еще более кратко (хотя отсутствие имен var -> менее ясно) - только для ударов:

// (spelled-fixed expiryDate); down to 23% loc, 42% char
const getUnexpiredFoodPerEmails = async ({expiryDateMax} = {
  expiryDateMax: new Date(new Date().addDays(7)),
})=> (await Promise.all((await db.collection('email').get())
  .map(data=> ({id: data.id, data: data.data()})).filter(o=> o.data)
  .map(async ({id, data: {email}})=> ({
    email,
    food: (await db.collection('email').doc(id).collection('list')
      .where('expiryDate', '<', expiryDateMax.getTime()).get())
      .filter(o=> o).map(o=> o.data()).filter(o=> o)
      .forEach(({name, expiryDate: time})=> ({name, time})),
  }))
)).filter(item=> item.food.length)

const add = async ()=> db.collection('sendGrid').add({
  response: await getUnexpiredFoodPerEmails(),
}).then(console.log).catch(console.error)

// ...or with names
const getUnexpiredFoodListForEmailId = async ({id, expiryDateMax} = {
  expiryDateMax: new Date(new Date().addDays(7)),
})=> (await db.collection('email').doc(id).collection('list')
  .where('expiryDate', '<', expiryDateMax.getTime()).get())
  .filter(o=> o).map(o=> o.data()).filter(o=> o)
  .forEach(({name, expiryDate})=> ({name, time: expiryDate}))

const getEmails = async ()=> (await db.collection('email').get())
  .map(data=> ({id: data.id, data: data.data()})).filter(o=> o.data)

const getUnexpiredFoodPerEmails = async ({expiryDateMax} = {
})=> (await Promise.all((await getEmails()).map(async ({id, data})=> ({
  email: data.email,
  food: await getUnexpiredFoodListForEmailId({id, expiryDateMax}),
})))).filter(item=> item.food.length)


const add = async ()=> db.collection('sendGrid').add({
  response: await getUnexpiredFoodPerEmails(),
}).then(console.log).catch(console.error)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...