Как ждать обещания отправки данных в массив в цикле forEach [реагировать] - PullRequest
1 голос
/ 21 октября 2019

Итак, я хочу использовать этот эффект:

useEffect(() => {
    return db.collection('users').orderBy('lastName').onSnapshot(snap => {
       const usersArray = []
       snap.forEach( async doc => {
         const user = doc.data()
         if(user.manager) {
            await Promise.all([
            db.collection('users').doc(user.manager).get(),
            db.collection('roles').doc(user.profile).get()
          ]).then(resp => {
                    const manager = resp[0].data()
                    const role = resp[1].data()
                    usersArray.push({
                      id: doc.id,
                      ...user,
                      manager: `${manager.lastName} ${manager.name} ${manager.middleName}`,
                      role: role.profile
                    })
                  })
         } else {
           await db
             .collection('roles')
             .doc(user.profile)
             .get().then(resp => {
              const role = resp.data()
              usersArray.push({
                id: doc.id,
                ...user,
                role: role.profile
              })
             })
         }
         usersArray.push({test: 'test'})
       })
       console.log(usersArray)
       setAgents(usersArray)
     })
   }, [])

Здесь я получаю список пользователей, затем я хочу получить для каждого пользователя данные об их роли в системе и их менеджерах, а также обновить пользователяОбъект и нажмите на массив. После этого я хочу сделать это. Но сейчас я всегда получаю пустой массив. В console.log из .then блоков данные верны.

Пожалуйста, помогите получить правильный код

enter image description here

1 Ответ

1 голос
/ 21 октября 2019

Это потому, что часть кода console.log выполняется синхронно до того, как какие-либо данные будут фактически записаны в массив. forEach свойство массива создает неблокирующий обратный вызов независимо от основного исполнения. Если вы хотите убедиться, что каждый элемент в snap обработан перед выполнением console.log, я рекомендую вместо этого использовать map, у карты есть уникальное свойство - фактически возвращать значение (в отличие от foreach). В этом случае это возвращаемое значение будет обещанием обратного вызова. Вы можете просто дождаться результата карты, чтобы убедиться, что все элементы были записаны в массив, или даже лучше вернуть фактические элементы внутри обратного вызова.

вот пример кода (для простоты я удалил usersArray.push({test: 'test'})который, я полагаю, не нужен)

useEffect(() => {
    return db.collection('users').orderBy('lastName').onSnapshot(snap => {
        const usersArrayPromise = snap.documents.map(doc => {
            const user = doc.data();

            if (user.manager) return Promise.all([
                db.collection('users').doc(user.manager).get(),
                db.collection('roles').doc(user.profile).get()
            ]).then(resp => {
                const manager = resp[0].data();
                const role = resp[1].data();
                return {id: doc.id, ...user, manager: `${manager.lastName} ${manager.name} ${manager.middleName}`, role: role.profile};
            });

            return db.collection('roles')
                .doc(user.profile)
                .get().then(resp => {
                    const role = resp.data();
                    return {id: doc.id, ...user, role: role.profile}
                })
        });
        Promise.all(usersArrayPromise).then(usersArray => {
            console.log(usersArray)
            setAgents(usersArray)
        });
    })
}, []);

Пожалуйста, обратите внимание на тот факт, что firebase QuerySnapshot не является реальным массивом, но реализует массив, такой как forEach свойство, если вы хотите использовать map, вы должны сделать snap.documents.map.

...