Цепочка запросов GET к WP REST API в Express

#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. Спасибо за это! Я смог найти сообщение в блоге, которое разбило его, и я смог скопировать код в сообщении в блоге и применить его к моей ситуации.