#node.js #express #google-cloud-firestore #promise #async-await
# #node.js #экспресс #google-облако-firestore #обещание #асинхронное ожидание
Вопрос:
Я изо всех сил пытаюсь понять обратные вызовы, обещания и асинхронность / ожидание.
Что я хочу сделать, так это прочитать CSV-файл внутри папки моего проекта, который содержит более 150 идентификаторов записей.
Для каждого из этих идентификаторов я хочу сделать запрос https GET, чтобы получить ответ JSON с моего сайта WordPress.
Затем для каждого из этих возвращаемых сообщений я хочу вставить их в свою базу данных Firestore.
Я пытаюсь понять, как правильно настроить функции обратного вызова.
Пожалуйста, помогите.
Заранее спасибо.
const express = require('express');
const router = express.Router();
const https = require("https");
const Recipe = require("../includes/newrecipe");
var admin = require('firebase-admin');
var serviceAccount = require("../service_key.json");
const collectionKey = "recipes"; //name of the collection
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: "<MY_FIRESTORE_URL>"
});
const firestore = admin.firestore();
const fs = require('fs');
const parse = require('csv-parser');
function prepareCsvData() {
return new Promise((resolve, reject) => {
//establish empty csvData array and filename to be referenced
var csvData = [];
var filename = 'wprm_recipe_ids.csv';
//read the csv file and push the data object into the array
fs.createReadStream(filename)
.pipe(parse(['ID']))
.on('data', (data) => csvData.push(data))
.on('end', () => { resolve(csvData); });
});
}
function getRecipeFromBlog(recipeId) {
return new Promise((resolve, reject) => {
//make the get request to my website to get the recipe
https.get('<MY_WEBSITE_URL>' recipeId, (response) => {
var body = "";
response.on('data', function (chunk) { body = chunk; });
response.on('end', () => {
var { recipe } = JSON.parse(body);
//build new recipe to be exported
var newRecipe = new Recipe(recipe);
resolve(newRecipe);
});
});
});
}
/* GET recipes. */
router.get('/', async (req, res, next) => {
//first prepare the csv data
//function returns a promise with the csv data
//that I can then use in the next step
const csvData = await prepareCsvData();
for (var i = 0; csvData.length < i; i ) {
getRecipeFromBlog(csvData[i].ID)
.then((newRecipe) => {
//when I have a recipe for a given recipe ID
//update database in firestore
firestore
.collection(collectionKey)
.doc("" newRecipe.id)
.set(newRecipe)
.then(function() {
console.log('document written');
});
});
}
res.send('done');
});
Комментарии:
1. вместо того, чтобы зацикливать и вставлять записи одну за другой, вы можете сделать, например, поместить все обещания в массив, т.Е. Все запросы get, и использовать promise.all ([массив обещаний]) с возвращаемым результатом, вы можете сопоставить данные в соответствии с вашими потребностями и выполнить массовую вставку в firestore!
2. @parseshyam интересно! Как бы мне провести рефакторинг кода, который я написал для этого?
Ответ №1:
Вам нужно сделать что-то вроде приведенного ниже: Поиграйте с этим, надеюсь, у вас все получится!
Дайте мне знать, если это сработало!
router.get("/", async (req, res, next) => {
const csvData = await prepareCsvData();
const recipePromises = [];
// check if data is empty or not
if (!csvData.length) {
return res.send("Empty data");
}
csvData.forEach((element) => {
recipePromises.push(getRecipeFromBlog(element.id));
});
// await for all promises parallelly.
const result = await Promise.all(recipePromises);
// Get a new write batch
const batch = db.batch();
result.forEach((recipe) => {
const ref = db.collection("recipes").doc(`${recipe.id}`);
batch.set(ref, recipe);
});
// Commit the batch
await batch.commit();
res.send("done");
});
Комментарии:
1. Спасибо за это! Я смог найти сообщение в блоге, которое разбило его, и я смог скопировать код в сообщении в блоге и применить его к моей ситуации.
Ответ №2:
Операционный код выглядит довольно близко к работе. Были ли протестированы функции возврата обещаний? Предполагая, что они работают, сначала оформите их как асинхронные…
async function prepareCsvData() {...}
async function getRecipeFromBlog(recipeId) {...}
Создайте еще одну функцию возврата обещаний, чтобы вставить много рецептов в firebase…
async function writeRecipesToFB(recipes) {
const collectionRef = collection(collectionKey);
const promises = recipes.map(recipe => {
return collectionRef.doc(`${recipe.id}`).set(recipe);
});
return Promise.all(promises)
}
Как следует из другого ответа, в качестве альтернативы, пакетная запись firebase — хорошая идея…
async function writeRecipesToFB(recipes) {
// as a set of promises
const collectionRef = collection(collectionKey);
const batch = db.batch();
recipes.forEach(recipe => {
const docRef = collectionRef.doc(`${recipe.id}`)
batch.set(docRef, recipe)
});
return batch.commit();
}
Теперь функцию express легко написать…
router.get('/', async (req, res, next) => {
const csvData = await prepareCsvData();
let promises = csvData.map(row => {
return getRecipeFromBlog(row.ID);
});
const recipes = await Promise.all(promises);
await writeRecipesToFB(recipes);
res.send('done');
});
Комментарии:
1. Спасибо за это! Я смог найти сообщение в блоге, которое разбило его, и я смог скопировать код в сообщении в блоге и применить его к моей ситуации.