Typescript массив обещаний, разрешающихся правильно в одном случае, но не в другом - PullRequest
1 голос
/ 18 марта 2019

У меня есть функция, которая отображает каждый элемент массива (в данном случае адреса электронной почты), получая массив обещаний, которые должны разрешаться для идентификаторов документов, автоматически генерируемых Firebase (т. Е. hAvJ3qPq821tq1q2rrEv, 0tjeKB1aW8jsOAse5fcP).

async function addMultipleParticipants(participant_emails: Array<string>) {
    console.log("Parallelizing participant processing");
    const promises = participant_emails.map(addParticipant);
    const document_ids = await Promise.all(promises);
    console.log("Final document ids: " + document_ids);
    return document_ids;
};

Вот функция, которая возвращает идентификаторы документов, в зависимости от того, может ли он найти существующий документ, связанный с адресом электронной почты или необходимый для создания нового документа.

async function addParticipant(email_address: string) {
    try {
        console.log("Querying for person");
        const query = await db.collection('people')
            .where('emails', 'array-contains', email_address)
            .limit(1);
        const querySnapshot = await query.get();
        if (!querySnapshot.empty) {
            console.log("Document exists for email " + email_address);
            // TODO: There is only one, so we shouldn't have to iterate
            querySnapshot.forEach(function(docRef: any) {
                console.log("New document id: " + docRef.id);
                const document_id = docRef.id;
                return document_id;
            });
        } else {
            console.log("Creating person with " + email_address);
            const fields = {emails: [email_address]};
            try {
                const docRef = await db.collection('people').add(fields);
                console.log("New document id: " + docRef.id);
                const document_id = docRef.id;
                return document_id;
            } catch (err) {
                console.log("Error adding document:", err);
            }
        }
    } catch (err) {
        console.log("Error getting document:", err);
    }
};

Когда все электронные письма участника неВ документах пока нет, функции работают должным образом, и console.log() выводит Final document ids: hAvJ3qPq821tq1q2rrEv, 0tjeKB1aW8jsOAse5fcP.

Однако, когда хотя бы один адрес электронной почты связан с существующим документом, обещания addParticipant() неразрешить что-либо и console.log() выводит Final document ids: ,.

В этом сценарии что мне нужно сделать, чтобы убедиться, что массив обещаний разрешается правильно в addMultipleParticipants()?

Ответы [ 3 ]

1 голос
/ 18 марта 2019

С Promise.all, если даже одно обещание не выполнится, вы не получите ни того, ни другого. Возьмите следующий пример:

const get = async () => {
    return Promise.all([
        new Promise((res, rej) => {
            setTimeout(() => {
                res('data!')
            }, 1000)
        }),
        new Promise((res, rej) => {
            setTimeout(() => {
                rej('error!')
            }, 2000)
        })
    ]);
}

const start = async () => {
    const response = await get()
    console.log(response)
}

start()

Вы просто получите ошибку Uncaught (in promise) error!.

Теперь, если вы хотите обойти это, чтобы получить хоть что-то взамен, вы можете потенциально использовать catch для каждого обещания, например, так:

const get = async () => {
    return Promise.all([
        new Promise((res, rej) => {
            setTimeout(() => {
                res('data!')
            }, 1000)
        }).catch(err => {
            return null
        }),
        new Promise((res, rej) => {
            setTimeout(() => {
                rej('error!')
            }, 2000)
        }).catch(err => {
            return null
        })
    ]);
}

const start = async () => {
    const response = await get()
    console.log(response)
}

start()

Это даст вам ["data!", null], и вы сможете обрабатывать null по своему усмотрению.

1 голос
/ 18 марта 2019

Разобрался с некоторыми подсказками от @basarat. Должен быть явный возврат для первого результата, а не цикл for: return querySnapshot.docs[0].id;. Полная измененная функция ниже:

async function addParticipant(email_address: string) {
    try {
        console.log("Querying for person");
        const query = await db.collection('people')
            .where('emails', 'array-contains', email_address)
            .limit(1);
        const querySnapshot = await query.get();
        if (!querySnapshot.empty) {
            console.log("Document exists for email " + email_address);
            return querySnapshot.docs[0].id;
        } else {
            console.log("Creating person with " + email_address);
            const fields = {emails: [email_address]};
            try {
                const docRef = await db.collection('people').add(fields);
                console.log("New document id: " + docRef.id);
                const document_id = docRef.id;
                return document_id;
            } catch (err) {
                console.log("Error adding document:", err);
            }
        }
    } catch (err) {
        console.log("Error getting document:", err);
    }
};
1 голос
/ 18 марта 2019

Когда все электронные письма участников еще не существуют в документах, функции работают должным образом, и console.log () выводит окончательные идентификаторы документов: hAvJ3qPq821tq1q2rrEv, 0tjeKB1aW8jsOAse5fcP

Это потому, что он разрешается в следующем операторе возврата:

return document_id;

Однако, когда хотя бы один адрес электронной почты связан с существующим документом, обещания addParticipant () ни к чему не приводят

Потому что ничего не возвращает. Последнее, что у вас есть:

        // TODO: There is only one, so we shouldn't have to iterate
        querySnapshot.forEach(function(docRef: any) {
            console.log("New document id: " + docRef.id);
            const document_id = docRef.id;
            return document_id;
        });

Это , а не возврат любого идентификатора. Это просто forEach. Пример исправления будет выглядеть так:

        // Return the id
        return querySnapshot[0].document_id;
...