1

How could I modify the following code to avoid nesting Promises?

The response of request-promise is needed to be inserted in Firestore.

I would also like to know how to have the jsonresponse available when the Firestore Promise is resolved to send the response.status to the caller aplication.

const functions = require('firebase-functions');
const rp = require('request-promise')
var admin = require("firebase-admin");

var serviceAccount = require("./service_key.json");

admin.initializeApp({
   credential: admin.credential.cert(serviceAccount),
   databaseURL: "https://melitest-5bc38.firebaseio.com"
});

let db = admin.firestore()

exports.customHttpRequest = functions.https.onRequest((request, response) => { 

const url = 'https://jsonplaceholder.typicode.com/users'

var options = {
    uri: url,
    method: "GET",
    json: true
};

rp(options).then((jsonresponse) => {
     for(var i = 0 ; i < jsonresponse.length; i++){
        var obj = jsonresponse[i]
        var docid = obj.id

        // Warning: Avoid nesting promises.eslint(promise/no-nesting)
        db.collection("scrapeusertest").doc(String(docid)).set(obj).then(() =>{ 
            console.log(`Data was upload to firestore and the response was: ${jsonresponse}`)
            response.status(200).send(jsonresponse);
        }).catch(error =>{
            console.log(`Error uploading data Firebase: ${error}`)
        });

    }
    return console.log("Data was send")
})
.catch((err) => {
    console.log('Error:', err)

});
    return null;
});
ManuelMB
  • 1,254
  • 2
  • 8
  • 16
  • 1
    What you are asking for is a code review and you should be asking such questions on the [Code Review Stack Exchange](https://codereview.stackexchange.com/) – Rob Mar 12 '20 at 14:00
  • Sorry, I didn't know about that. Is there any way to move my question from here to there or should I delete it from here? – ManuelMB Mar 12 '20 at 14:47
  • You can't move it so just delete it. – Rob Mar 12 '20 at 15:03
  • I delete it but undelete it again because I posted it where do you suggested and there someone told me that it was not the right place to do it. https://codereview.stackexchange.com/questions/238777/how-to-avoid-nesting-promises-and-send-response-status-after-insert-data-in-fire – ManuelMB Mar 12 '20 at 23:17
  • Are you open to converting it to an async function? – Jacob Mar 12 '20 at 23:22
  • 1
    it looks like your code is sending response after first resolved promise so it wont work anyway. reason te linter is complaining is .then inside the .then, chain it or async/await instead – Lawrence Cherone Mar 12 '20 at 23:24
  • If it works ok in Firebase, yes I do, because I would like to have this code working as soon as possible, I tried it but I get Error detected in customHttpRequest, but maybe I did not implement it right. But I am also interested in a way to solve this issue without the approach you suggested just for increase my knowledge, your answer will be very welconme. – ManuelMB Mar 12 '20 at 23:27
  • The much more important problem in your code is that you are creating multiple promises inside a loop without waiting for them (the `Data was send` is logged *before* any data was sent), and that you are calling `response.status(…).send(…)` multiple times while one can only respond once to a request. – Bergi Mar 12 '20 at 23:51
  • The general question appears to be a duplicate of [How do I access previous promise results in a .then() chain?](https://stackoverflow.com/q/28250680/1048572) – Bergi Mar 12 '20 at 23:52
  • use promise and used the promise all – Mohammed Al-Reai Mar 12 '20 at 23:56

2 Answers2

0

Easiest option is to use an async function:

const db = admin.firestore()

exports.customHttpRequest = functions.https.onRequest(async (request, response) => { 

  const url = 'https://jsonplaceholder.typicode.com/users'
  const options = {
    uri: url,
    method: "GET",
    json: true
  };

  const jsonresponse = await rp(options);

  await Promise.all(jsonresponse.map(async obj => {
    const docid = obj.id
    try {
      await db.collection("scrapeusertest").doc(String(docid)).set(obj);
    } catch (error) {
      console.log(`Error uploading data Firebase: ${error}`);
    }
  }));

  console.log(`Data was upload to firestore and the response was: ${jsonresponse}`);
  response.status(200).send(jsonresponse);
});
Jacob
  • 77,566
  • 24
  • 149
  • 228
0

use promise and used the promise all as this shape

    const res= await rp(options);
//should be soure the res is array
     await Promise.all(res.map( async item =>{
    const id=item.id;
     try {
          await db.collection("scrapeusertest").doc(String(id)).set(item);
        } catch (error) {
    //what action when handle wrror
        }))})

you can use the res as what you need

Mohammed Al-Reai
  • 2,344
  • 14
  • 18