0

I want to use async/await inside this promise.allSettled to convert currency by fetching some api or databased stored currency rates.

like i want to use await here like this, but not sure where to put async.

arrcars["price_client"] = await forexService.convertCurrency(item.regular_price, "EUR", userCurrency);

I have posted my entire code. need some guidance here.

This is controller

    const httpStatus = require('http-status');
    const pick = require('../utils/pick');
    const catchAsync = require('../utils/catchAsync');
    const async = require('async');
    const axios = require('axios');
    const { talixoService } = require('../services');
    const { iwayService } = require('../services');
    const { forexService } = require('../services');
    
        const searchCars = function (req, res) {
            const talixoData = talixoService.findTalixoCars(req.body) //call talixo api in services
            const iwayData = iwayService.findiWayCars(req.body) //call iwaytransfers api in services
        
            if (req.query.usercurrency) {
                var userCurrency = req.query.usercurrency;
            } else {
                var userCurrency = "INR";
            }
        
            return Promise.allSettled([talixoData, iwayData]).then(([restalixo, resiway]) => {
                const taxiresults = [];
                if (restalixo.status === "fulfilled") {
                    //console.log(restalixo.value.data);
                    if (restalixo.value.data.taxis.length) {
                        restalixo.value.data.taxis.forEach(function (item) {
                            arrcars = {};
                            arrcars["carname"] = item.car_model;
                            arrcars["originprice"] = item.regular_price;
                            arrcars["price_client"] = forexService.convertCurrency(item.regular_price, "EUR", userCurrency);
                            arrcars["discountprice_client"] = forexService.convertCurrency(item.discount_price, "EUR", userCurrency);
                            arrcars["packageId"] = item.id;
                            arrcars["image"] = item.image_url;
                            arrcars["freewaittime"] = item.included_waiting_time;
                            arrcars["maxluggage"] = item.luggage;
                            arrcars["maxpassengers"] = item.seats;
                            arrcars["discountprice"] = item.discount_price;
                            arrcars["vehicletype"] = item.vehicle_type;
                            arrcars["vendor"] = "Talixo";
                            arrcars["vehicleremarks"] = "Or Similar";
                            taxiresults.push(arrcars);
                        });
                    }
                    if (restalixo.value.data.limousines.length) {
                        restalixo.value.data.limousines.forEach(function (item) {
                            arrcars = {};
                            arrcars["carname"] = item.car_model;
                            arrcars["originprice"] = item.regular_price;
                            arrcars["price_client"] = forexService.convertCurrency(item.regular_price, "EUR", userCurrency);
                            arrcars["discountprice_client"] = forexService.convertCurrency(item.discount_price, "EUR", userCurrency);
                            arrcars["packageId"] = item.id;
                            arrcars["image"] = item.image_url;
                            arrcars["freewaittime"] = item.included_waiting_time;
                            arrcars["maxluggage"] = item.luggage;
                            arrcars["maxpassengers"] = item.seats;
                            arrcars["discountprice"] = item.discount_price;
                            arrcars["vehicletype"] = item.vehicle_type;
                            arrcars["vendor"] = "Talixo";
                            arrcars["vehicleremarks"] = "Or Similar";
                            taxiresults.push(arrcars);
                        });
                    }
                }
                //iwaytransfers supplier data
                if (resiway.status === "fulfilled") {
                    //console.log(resiway.value.data);
                    if (resiway.value.data.result.length) {
                        resiway.value.data.result.forEach(function (item) {
                            var imgsrc = "https://iwayex.com/images/cars/";
                            arrcars = {};
                            arrcars["carname"] = item.car_class.models[0];
                            arrcars["originprice"] = item.price;
                            arrcars["price_client"] = forexService.convertCurrency(item.price, "EUR", userCurrency);
                            arrcars["discountprice_client"] = forexService.convertCurrency(item.price, "EUR", userCurrency);
                            arrcars["packageId"] = item.price_uid;
                            arrcars["image"] = imgsrc + item.car_class.photo;
                            arrcars["freewaittime"] = item.allowable_time;
                            arrcars["maxluggage"] = "";
                            arrcars["maxpassengers"] = item.car_class.capacity;
                            arrcars["discountprice"] = item.price;
                            arrcars["vehicletype"] = item.vehicle_type;
                            arrcars["vendor"] = "iway Transfers";
                            arrcars["vehicleremarks"] = "Or Similar";
                            taxiresults.push(arrcars);
                        });
                    }
                }
        
                if (taxiresults.length) {
                    sortedresult = taxiresults.sort(function (a, b) {
                        return a.discountprice - b.discountprice;
                    });
                    res.status(200).send(sortedresult)
                }else{
                    res.status(200).send({})
                }
            });
        }

module.exports = {
    searchCars
}

this is the result sample i am getting.

{
carname: "Toyota Prius"
discountprice: 27.5
discountprice_client: {}
freewaittime: 45
image: "https://static.talixo.de/images/vehicles/economy.png"
maxluggage: 3
maxpassengers: 3
originprice: 27.5
packageId: "16021"
price_client: {}
vehicleremarks: "Or Similar"
vehicletype: "limo"
vendor: "Talixo"
}

here the problem is, i expect

discountprice_client: Converted Number, 

but i am getting blank object there as can be seen in above result sample. i have been using this in another function with async await, then it works fine. but in this case, i am not sure how to put async/await.

this is inside

forex.service.js

    const convertCurrency = async (amount, basecurrency, reqCurrency) => {
        if (basecurrency === reqCurrency) {
            let result = Math.round(amount) + ' ' + reqCurrency;
            return result;
        } else {
            const reqForexData = await getForexbyISO(reqCurrency);
            const baseForexData = await getForexbyISO(basecurrency);
            const amountInUSD = amount / baseForexData.forexRate;
            const amountInreqC = amountInUSD * reqForexData.forexRate;
            let result = Math.round(amountInreqC) + ' ' + reqCurrency;
            return result;
        }
    }

module.exports = {
    convertCurrency
}
5
  • JS doesn't allow top level awaits. However, typescript does. If using TS is not an option, you can use IIFE with the async code inside Commented Jul 8, 2022 at 14:54
  • 2
    @kanuos JS does, in ES modules, and TS only does if it's emitting to such a format. Commented Jul 8, 2022 at 14:55
  • OP didn't specify the problem Commented Jul 8, 2022 at 15:07
  • @KonradLinkowski i have updated my question with exact problem. Commented Jul 8, 2022 at 15:16
  • 1
    Not part of your question, but forex.service.js calls getForexbyISO twice in serial. You might want to get both promises (i.e. don't use await) and then do Promise.all([reqForexDataPromise, baseForexDataPromise]).then(([reqForexData, baseForexData]) => ...) so that it's done in parallel instead. Same for the function that searchCars passes to array.forEach. Commented Jul 8, 2022 at 17:33

2 Answers 2

2

You could do:

// notice the `async` before `function`
resiway.value.data.result.forEach(async function (item) {
  // ...
  arrcars = {};
  arrcars["price_client"] = await forexService.convertCurrency(item.price, "EUR", userCurrency);
  arrcars["discountprice_client"] = await forexService.convertCurrency(item.price, "EUR", userCurrency);
  // ...
});

But it can lead to unexpected behaviours

I would do this

// add missing `async`
return Promise.allSettled([talixoData, iwayData]).then(async ([restalixo, resiway]) => {
  // ...
  // await for all loop at once to avoid odd side effects
  await Promise.all(resiway.value.data.result.map(async function (item) {
    // ...
    arrcars = {};
    arrcars["price_client"] = await 
  forexService.convertCurrency(item.price, "EUR", userCurrency);
    arrcars["discountprice_client"] = await 
  forexService.convertCurrency(item.price, "EUR", userCurrency);
    // ...
  }));
  // ...
});
Sign up to request clarification or add additional context in comments.

8 Comments

I tried your second option but i get this error. await Promise.all(resiway.value.data.result.map(async function (item) { ^^^^^ SyntaxError: await is only valid in async function
and if i use first option, then in whole result, i get blank object.
After update i am getting this error. error: TypeError: undefined is not iterable (cannot read property Symbol(Symbol.iterator)) at Function.all (<anonymous>) at /Users/rishavkumar/Documents/GitHub/TTCBackendAPI/src/controllers/cars.controller.js:135:31 at processTicksAndRejections (internal/process/task_queues.js:95:5)
I have no idea what can cause it
i am checking. atleast i have learned how to use async/await, thank you for that
|
1

simplify your services

You are writing the Talixo and Iway services but using the result is difficult. Instead of returning the entire response, return the fields that matter to your consumer -

// services/talixo_service.js

async function findTalixoCars(query) {
  const result = await // ... perform request
  return result.value.data ?? [] // <- return relevant data
}

Notice we use return _____ ?? [] to ensure that an array is always returned -

// services/iway_service.js

async function findiWayCars(query) {
  const result = await // ... perform request
  return result.value.data.result ?? [] // <- return relevant data
}

searchCars

With simplified services, we write a high-level implementation of searchCars. Instead of using Promise.allSettled we can use .catch(_ => []) on potential failures to guarantee we have an array result. This reduces the need for logical checks and code branches to make the code more declarative and less prone to errors -

async function searchCars(req, res) {
  const [talixo, iway] = await Promise.all([
    talixoService.findTalixoCars(req.body).catch(_ => []),
    iwayService.findiWayCars(req.body).catch(_ => [])
  ])

  const userCurrency = req.query.usercurrency ?? "INR"

  const taxis = [
    ...await Promise.all(talixo.taxis.map(item => fromTalixo(item, userCurrency))),
    ...await Promise.all(talixo.limousines.map(item => fromTalixo(item, userCurrency))),
    ...await Promise.all(iway.map(item => fromIway(item, userCurrency)))
  ]
  
  res.status(200).send(
    taxis.sort((a,b) => a.discountprice - b.discountprice)
  )
}

data mapping

The search function above depends on fromTalixo and fromIway. These mapping functions reduce code complexity and repitition and gives us a good conceptual model for how data gets mapped from the remote services to our local data -

async function fromTalixo(data, userCurrency) {
  return {
    carname: data.car_model,
    originprice: data.regular_price,
    price_client: await forexService.convertCurrency(data.regular_price, "EUR", userCurrency),
    discountprice_client: await forexService.convertCurrency(data.discount_price, "EUR", userCurrency),
    packageId: data.id,
    image: data.image_url,
    freewaittime: data.included_waiting_time,
    maxluggage: data.luggage,
    maxpassengers: data.seats,
    discountprice: data.discount_price,
    vehicletype: data.vehicle_type,
    vendor: "Talixo",
    vehicleremarks: "Or Similar",
  }
}
async function fromIway(data, userCurrency) {
  return {
    carname: data.car_class.models[0],
    originprice: data.price,
    price_client: await forexService.convertCurrency(data.price, "EUR", userCurrency),
    discountprice_client: await forexService.convertCurrency(data.price, "EUR", userCurrency),
    packageId: data.price_uid,
    image: `https://iwayex.com/images/cars/${data.car_class.photo}`,
    freewaittime: data.allowable_time,
    maxluggage: "",
    maxpassengers: data.car_class.capacity,
    discountprice: data.price,
    vehicletype: data.vehicle_type,
    vendor: "iway Transfers",
    vehicleremarks: "Or Similar",
  }
}

4 Comments

very clean code ! thanks for explaining me this. learnt more today. just edited few typos in your code.
thanks for catching those. happy to assist :D
Hi, One more issue, i face with your code is, if talixo return blank result, like when i print talixo, in some searches it return "[]" . then i get this issue - error: TypeError: Cannot read property 'map' of undefined.. How to solve such issues here.
if talixo (or any service) returns a null or empty result, you should return [] instead, which means there is a zero list of cars. That would look like return result.value.data ?? []. Because all arrays have map method, the TypeError will go away. I updated the post to reflect this change.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.