Итак, я с радостью использую async / await, поскольку узел 8 поддерживается в Firebase Cloud Functions. Однако я борюсь с одной вещью. При использовании вызываемых функций сообщается, что вы должны вернуть обещание в функции, иначе оно не будет работать правильно. При использовании необработанных обещаний мне ясно, как их использовать:
exports.createBankAccount = functions.region('europe-west1').https.onCall((data, context) => {
return promiseMethod().then((result) => {
return nextPromise(result);
}).then((result) => {
return result;
}).catch((err) => {
// handle err
})
});
Но теперь, с async await, я не уверен, как вернуть эту «цепочку обещаний»:
exports.createBankAccount = functions.region('europe-west1').https.onCall(async (data, context) => {
const res1 = await promiseMethod();
const res2 = await nextPromise(res1);
return res2;
// ??? Where to return the promise?
});
Кто-нибудь знает?
Хорошо, но журналы на консоли firebase говорят мне, что я вызываю функцию 2 раза, хотя на самом деле я вызываю ее только 1 раз. Я думал, что это как-то связано с неправильным возвратом этих обещаний.
Вы можете легко создать пример проекта и проверить утверждения ответов. Просто создайте функцию, которая будет ждать 10 секунд, а затем вернет результат. Если вы вызовете эту функцию с помощью await, функция firebase правильно подождет 10 секунд, а затем вернет результат.
Я добавил к своему ответу авторитетный образец кода от команды google-firebase. Если этого недостаточно, я не знаю.



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Функции HTTP не возвращают обещание. Они просто отправляют результат. Вам все равно нужно правильно использовать обещания, чтобы отправить результат, но возвращаемое значение не требуется. Функции HTTP завершаются при отправке ответа. См. документация для более подробной информации:
Terminate HTTP functions with res.redirect(), res.send(), or res.end().
То же самое и с .onCall-функциями? Когда я вызываю свою функцию один раз, регистратор на панели управления firebase сообщает, что моя функция вызывается дважды. Я считал, что это как-то связано с возвратом этих обещаний.
Нет, вызываемые функции требуют, чтобы вы возвращали обещание, которое разрешается с данными, которые вы хотите отправить клиенту. Это должно быть ясно из документации.
Именно поэтому мой первоначальный вопрос относится к вызываемым функциям: как вернуть это обещание при использовании async / await
Итак, вы отредактировали вопрос, чтобы показать вызываемую функцию вместо функции HTTP, которая у вас была изначально. Фактически вам задают другой вопрос вместо первого, что на самом деле не так, как работает Stack Overflow. Если вы полностью измените условия вопроса, вы запутаете всех.
Я думал, что HTTPS также должен возвращать обещание; для меня это была мелочь, но факт отмечен.
HTTPS и вызываемый очень разные способы отправки данных клиенту. Найдите время, чтобы изучить документацию и образцы, чтобы прояснить это.
@DougStevenson В документе firebase указано, что вы можете вернуть обещание для HTTP-функций и просто убедитесь, что это обещание в конечном итоге вызовет response.end() или что-то подобное.
При необходимости просто конвертируйте в Promise.
Т.е. Если nextPromise возвращает обещание:
exports.createBankAccount = functions.region('europe-west1').https.onCall(async (data, context) => {
const res1 = await promiseMethod();
return nextPromise(res1);
});
С другой стороны, если nextPromise является асинхронной функцией, просто преобразуйте ее в Promise:
exports.createBankAccount = functions.region('europe-west1').https.onCall(async (data, context) => {
const res1 = await promiseMethod();
return Promise.resolve(nextPromise(res1));
});
вы также можете преобразовать результат:
exports.createBankAccount = functions.region('europe-west1').https.onCall(async (data, context) => {
const res1 = await promiseMethod();
const res2 = await nextPromise(res1);
return Promise.resolve(res2);
});
Это совершенно неуместно: асинхронная функция вернет Promise вызывающей стороне при первом «ожидании», которое в конечном итоге разрешит все, что вы вернете в операторе return. Заключение этого обещания в другое обещание ничего не дает.
Я привел 3 примера. первый оптимален, два других - нет, и здесь просто для пояснения, что есть несколько способов сделать то же самое.
Promise.resolve совершенно не нужен и вводит в заблуждение. Вы можете просто удалить его в обоих случаях, без каких-либо минусов. Это просто ненужный код, отвлекающий от работы асинхронных функций.
Когда вы пишете асинхронную функцию, код фактически выйдет из функции и вернет обещание при первом встретившемся ожидании. Весь код после ожидания будет преобразован в then ().
Таким образом, для firebase написание кода с помощью async / await отлично сохраняется и, по моему опыту, даже менее подвержено ошибкам, так как я могу легче структурировать try & catch в своем коде!
Просто запустите это в своей консоли:
async function iAmAsync() {
await new Promise(r => window.setTimeout(r, 1000))
return 'result'
}
let x = iAmAsync()
console.info(x)
Напечатает: Promise{<resolved>: "result"}
И поскольку мой ответ был отклонен, вот авторский образец кода самой команды Google Firebase:
https://github.com/firebase/functions-samples/blob/master/quickstarts/uppercase/functions/index.js
exports.addMessage = functions.https.onRequest(async (req, res) => {
// [END addMessageTrigger]
// Grab the text parameter.
const original = req.query.text;
// [START adminSdkPush]
// Push the new message into the Realtime Database using the Firebase Admin SDK.
const snapshot = await admin.database().ref('/messages').push({original: original});
// Redirect with 303 SEE OTHER to the URL of the pushed object in the Firebase console.
res.redirect(303, snapshot.ref.toString());
// [END adminSdkPush]
});
Кажется, нам нужно дождаться нескольких обещаний следующим образом:
const listOfAsyncJobs = [];
listOfAsyncJobs.push(createThumbnail(1, ...));
listOfAsyncJobs.push(createThumbnail(2, ...));
listOfAsyncJobs.push(createThumbnail(3, ...));
...
return Promise.all(listOfAsyncJobs); // This will ensure we wait for the end of the three aync tasks above.
Из асинхронного метода все, что вы возвращаете, автоматически оборачивается в обещание. например
const myFun = async () => {return 5}
myFun();
// Output in the console
Promise {<fulfilled>: 5}
И вы можете связать с возвращенным результатом, так как это обещание
Другой пример с улучшением, предложенным в другом ответе
const myFun4 = async () => {
const myNum = await new Promise(r => window.setTimeout(() => r(5), 1000));
const myNum2 = await new Promise(r => window.setTimeout(() => r(5), 1000));
return myNum + myNum2;
}
myFun4().then((n) => console.info(n));
// Output
10
Возвращаемое значение функции async-await - Promise.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function#return_value
Итак, на самом деле вы вернули цепочку обещаний.
const nextPromise = () => {
console.info('next promise!');
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('next promise result')
}, 3000)
});
}
const promiseMethod = () => {
console.info('promise!');
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('promise result');
}, 2000)
});
}
exports.createBankAccount = functions.https.onCall((data, context) => {
return promiseMethod().then((result) => {
return nextPromise(result);
}).then((result) => {
return result;
}).catch((err) => {
// handle err
console.info(err);
})
});
exports.createBankAccountAsync = functions.https.onCall(async (data, context) => {
const result = await promiseMethod();
const res = await nextPromise(result);
return res;
});
Я создал тестовый проект на firebase, и оба вызова функций дают одинаковые журналы.

Решение в этом случае - Promise.all().
exports.createBankAccount = functions.region('europe-west1').https.onCall(async (data, context) => {
const promises = [];
const res1 = await promiseMethod();
const res2 = await nextPromise(res1);
promises.push(res1);
promises.push(res2);
// Here's the return of the promises
return Promise.all(promises).catch(error => console.error(error));
});
Вы можете найти больше информации об обещаниях в этой статье на freecodecamp.org/promise-all
Вы прибили это своим примером кода.
Async / await - это просто новый способ обещания. Их можно использовать взаимозаменяемыми.
Вот пример обещания и async / await одной и той же функции.
Этот
exports.createBankAccount = functions.region('europe-west1').https.onCall((data, context) => {
return promiseMethod().then((result) => {
return nextPromise(result);
}).catch((err) => {
// handle error here
})
});
эквивалентно этому:
exports.createBankAccount = functions.region('europe-west1').https.onCall(async (data, context) => {
try {
const result = await promiseMethod();
return nextPromise(result); // You are returning a promise here
}catch(e) {
// handle error here
}
});
Обратите внимание, что в обоих случаях вы возвращаете обещание в конце. Возвращаемое значение этой функции onCall будет тем же, что и nextPromise(result). Поскольку вы возвращаете nextPromsie(result), ждать его не нужно.
Чтобы увидеть кодовое решение вашего вопроса, посмотрите ответ Dshukertjr.
Если вы хотите понять, как вернуть «цепочку обещаний» с помощью async / await, вот ваш ответ:
Вы не можете! Почему ? Потому что await используется для ожидания завершения обещания. После того, как await вернет значение, их больше не будет Promise.
Поэтому, если вы абсолютно хотите вернуть обещание с помощью await, вы можете дождаться одной из двух функций, которые возвращают обещания, но не обоих.
Вот два способа сделать это:
А:
exports.createBankAccount = functions.region('europe-west1').https.onCall(async (data, context) => {
try {
const result = await promiseMethod();
return nextPromise(result); // You are returning a promise here
}catch(e) {
// handle error here
}
});
B:
exports.createBankAccount = functions.region('europe-west1').https.onCall(async (data, context) => {
return promiseMethod().then(async (result) => {
return await nextPromise(result);
}).catch((err) => {
// handle err
})
});
Единственное различие между A и B состоит в том, что A ожидает завершения "PromiseMethod", прежде чем вернуть Promise. В то время как B возвращает Promise сразу после вызова.
Поскольку вам нужно вернуть обещание, вы можете создать объект обещания и разрешить / отклонить (вернуть) свой ответ из api после обработки всех обещаний.
Опция 1:
exports.createBankAccount = functions.region('europe-west1').https.onCall((data, context) => {
return new Promise(async (resolve, reject) => {
try {
const res1 = await promiseMethod();
const res2 = await nextPromise(res1);
// This will return response from api
resolve(res2);
}
catch (err) {
// Handle error here
// This will return error from api
reject(err)
}
})
});
Вариант 2:
exports.createBankAccount = functions.region('europe-west1').https.onCall((data, context) => {
return new Promise(async (resolve, reject) => {
const res1 = await promiseMethod();
const res2 = await nextPromise(res1);
// This will return response from api
resolve(res2);
})
.then((val) => val)
.catch((err) => {
// Handle error here
// This will return error from api
return err
})
});
К сожалению, функции исполнителя обещаний не должны быть асинхронными.
«Я не уверен, как вернуть эту« цепочку обещаний »:» - Точно так же, как вы сделали в написанном вами коде.