-3

Behavior I'm getting from the code shown in the attached print screen: my FOR loop executes and then the two 'LET' (i.e. let path_get... and let get = https.get...).

Problem: when the 2nd LET is executed, the code goes back to the FOR, without ever executing the function storeCarData.

My intention: every time that the second LET executes (GET call), I want storeCarData to to be executed taking as inputs the result of this GET. This is probably an async/await question but I need your help in pointing out where to insert the asyncs/awaits if possible. I need to execute ALL of the functions in sequence (i.e one executes only after the previous one has returned).

Could you please help me here? In summary: when I execute the GET I want storeCarData to run, using the GET outputs as inputs for the function.

Thank you in advance.

Diego PS: full code below

const https = require('https');
const url = require('url');

exports.handler = (req, res) => {

  var page_number = (typeof(req.body.state) == 'undefined' || typeof(req.body.state.page_number) ==  'undefined') ? 1 : req.body.state.page_number+1;
  var kbb_vehicles = [];
  var price_type = [2,3];
  var price_description = ['C2C','C2B'];
  // PRICE TYPES
  // "ID": 1, "Typical Dealer Asking Price"
  // "ID": 2, Private Party Value (for owners)"
  // "ID": 3, "Trade-In Value"
  // "ID": 4, "Private Party Value (for shoppers)"
  
  var epoch = new Date().getTime();
  console.log(`Date time is ${epoch}`);
  var path_get_all_vehicles = `/Gps/GetAllVehicle?securityToken=${req.body.secrets.securityToken}&language=pt-BR&withCount=true&pageNumber=${page_number}&records=100`;


  // GET ALL CARS
  let get = https.get(
    {
    hostname: 'api.kbb.com.br',
    path: path_get_all_vehicles
    },
    (getRes) => {
      var reply = "";
      getRes.on("data", (chunk) => {
        (reply += chunk);
        }
      );
      getRes.on("end", () => {
        var obj = "";
        var obj = JSON.parse(reply);
        gotCars(obj.Response.VehicleList);
        }
      );
    }
  );

  function gotCars(carsJustObtained) {
    // PASS ALL CARS

    for (var car of carsJustObtained) {
      // NOW FOR A GIVEN CAR...      

          for (var i=0; i<=1; i++){

            // ...GET PRICES TYPES 2 AND 3
            let path_get_all_prices = `/Gps/GetAllVehiclePrices?vehicleID=${car.ID}&securityToken=${req.body.secrets.securityToken}&vehiclePriceTypeID=${price_type[i]}`;
            // console.log(`path_get_all_prices is ${path_get_all_prices}`);
            // console.log(`i is ${i}`);
            // console.log(`price_type[i] is ${price_type[i]}`);

            let get = https.get(
              {
              hostname: 'api.kbb.com.br',
              path: path_get_all_prices
              },
              (getRes) => {
                var reply = "";
                getRes.on("data", (chunk) => {
                  (reply += chunk);
                  }
                );
                getRes.on("end", () => {
                  var obj = "";
                  var obj = JSON.parse(reply);
                  // WRITE PRICES INTO OBJECT >> APPEND RESULTS
                  var grades_array = obj.Response;
                  storeCarData(car, grades_array,i);
                });
              }
            );
            
            // price_description[0] is equal to C2C
            // price_description[1] is equal to C2B
            // grades_array[0] corresponds to VehicleGrade=2, thus "Fair"
            // grades_array[1] corresponds to VehicleGrade=3, thus "Good"
            // grades_array[2] corresponds to VehicleGrade=2, thus "Excellent"
            
            function storeCarData(car, grades_array,i){
              var carData = {
                timestamp: epoch,
                ID  : car.ID,
                BrandID : car.BrandID,
                ModelID : car.ModelID,
                VersionID   : car.VersionID,
                BrandName   : car.BrandName,
                ModelName   : car.ModelName,
                VersionName : car.VersionName,
                Year    : car.Year,
                MSRP    : car.MSRP,
                ISV : car.ISV,
                TransportCost   : car.TransportCost,
                AdminCost   : car.AdminCost,
                RoadTax : car.RoadTax,
                StartYearImported   : car.StartYearImported,
                EndYearImported : car.EndYearImported,
                BodyTypeID  : car.BodyTypeID,
                CC  : car.CC,
                HP  : car.HP,
                KW  : car.KW,
                Torque  : car.Torque,
                NrCilinders : car.NrCilinders,
                TransmissionTypeID  : car.TransmissionTypeID,
                Gears   : car.Gears,
                NrDoors : car.NrDoors,
                NrSeats : car.NrSeats,
                FuelTypeID  : car.FuelTypeID,
                EngineTypeID    : car.EngineTypeID,
                ExhaustTypeID   : car.ExhaustTypeID,
                DistanceAxis    : car.DistanceAxis,
                Volume  : car.Volume,
                DriveTrainID    : car.DriveTrainID,
                Weight  : car.Weight,
                WeightCarriage  : car.WeightCarriage,
                VehicleTypeID   : car.VehicleTypeID,
                VehicleCilindersID  : car.VehicleCilindersID,
                FirstMediaURL   : car.FirstMediaURL,
                CombinedConsumption : car.CombinedConsumption,
                UrbanConsumption    : car.UrbanConsumption,
                ExtraUrbanConsumption   : car.ExtraUrbanConsumption,
                MaximumSpeed    : car.MaximumSpeed,
                FuelTankRangeKM : car.FuelTankRangeKM,
                FuelTankCapacity    : car.FuelTankCapacity,
                Acceleration0to100  : car.Acceleration0to100,
                EmissionsCO2    : car.EmissionsCO2,
                FirstMktCategoryID  : car.FirstMktCategoryID,
                PriceType   : car.PriceType,
                Grade   : car.Grade,
                TAMileage   : car.TAMileage,
                TAAge   : car.TAAge,
                AVMileage   : car.AVMileage,
                AuctionPrice    : car.AuctionPrice,
                AskingPrice : car.AskingPrice,
                TradeInPrice    : car.TradeInPrice,
                PrivatePartyPrice   : car.PrivatePartyPrice,
                C2CPrice    : car.C2CPrice,
                VersionCatalogID    : car.VersionCatalogID,
                ExternalReference   : car.ExternalReference,
                VehicleSeriesName   : car.VehicleSeriesName,
                VehicleVersionNameLong  : car.VehicleVersionNameLong,
                FirstMediaCompleteURLLarge  : car.FirstMediaCompleteURLLarge,
                FirstMediaCompleteURLMedium : car.FirstMediaCompleteURLMedium,
                FirstMediaCompleteURLSmall  : car.FirstMediaCompleteURLSmall,
                BedLength   : car.BedLength,
                CabType : car.CabType,
              }
              storePrices(grades_array,carData.timestamp, carData.ID, price_description[i]);
            };

            function storePrices(grades_array, timestamp, carID, price_description){
              var prices = {
                timestamp:timestamp,
                carID:carID,
                [`PriceFPP_FairCondition_${price_description}`]: grades_array[0].VehiclePrices.FPP,
                [`PriceFPP_GoodCondition_${price_description}`]: grades_array[1].VehiclePrices.FPP,
                [`PriceFPP_ExcellentCondition_${price_description}`]: grades_array[2].VehiclePrices.FPP,
                [`PriceLow_FairCondition_${price_description}`]: grades_array[0].VehiclePrices.PriceLow,
                [`PriceLow_GoodCondition_${price_description}`]: grades_array[1].VehiclePrices.PriceLow,
                [`PriceLow_ExcellentCondition_${price_description}`]: grades_array[2].VehiclePrices.PriceLow,
                [`PriceHigh_FairCondition_${price_description}`]: grades_array[0].VehiclePrices.PriceHigh,
                [`PriceHigh_GoodCondition_${price_description}`]: grades_array[1].VehiclePrices.PriceHigh,
                [`PriceHigh_ExcellentCondition_${price_description}`]: grades_array[2].VehiclePrices.PriceHigh,
                [`EquipmentAdjustedPrice_FairCondition_${price_description}`]: grades_array[0].VehiclePrices.EquipmentAdjustedPrice,
                [`EquipmentAdjustedPrice_GoodCondition_${price_description}`]: grades_array[1].VehiclePrices.EquipmentAdjustedPrice,
                [`EquipmentAdjustedPrice_ExcellentCondition_${price_description}`]: grades_array[2].VehiclePrices.EquipmentAdjustedPrice,
                [`BaseDiscountedPrice_FairCondition_${price_description}`]: grades_array[0].VehiclePrices.BaseDiscountedPrice,
                [`BaseDiscountedPrice_GoodCondition_${price_description}`]: grades_array[1].VehiclePrices.BaseDiscountedPrice,
                [`BaseDiscountedPrice_ExcellentCondition_${price_description}`]: grades_array[2].VehiclePrices.BaseDiscountedPrice
              }
            };
          };
    };
  };
}

enter image description here

DiF
  • 37
  • 4
  • 2
    [Why not upload images of code/errors when asking a question?](https://meta.stackoverflow.com/questions/285551/why-not-upload-images-of-code-errors-when-asking-a-question) – charlietfl Mar 07 '21 at 23:27
  • 2
    Please redo your question with plain text code. Among other things, this allows people who want to provide answers to copy/paste sections of code into answer. – jfriend00 Mar 07 '21 at 23:29
  • Perfect. I saw the references and couldn't determine an answer from there. Perhaps what could be useful is if people could point me where to put async and await - I'm very familiar with the concept. – DiF Mar 07 '21 at 23:40
  • From review: You may need the _canonical_ question about [How do I return the response from an asynchronous call?](https://stackoverflow.com/q/14220321/5217142) and its [top rated answer](https://stackoverflow.com/a/14220323/5217142) for an example of `async/await` usage. You **will** need to promisify `https.get` to return a promise that you can `await` before calling `storeCarData`. Possibly modify one of the answers to [nodejs - How to promisify http.request?...](https://stackoverflow.com/q/38533580/5217142) for `https`, or search NPM using keywords such as _https get promise_. – traktor Mar 08 '21 at 00:55
  • Sorry but I’m new to the community and don’t know what you mean @traktor. – DiF Mar 08 '21 at 01:27
  • `getRes.on("end"...` callbacks are not called synchronously: `https.get` only initiates the request. To set up an asynchronous loop, code needs to `await` a **promise** for the `https.get` data returned from the`path_get_all_prices` url. This means creating a promise for the data to be resolved later, inside the request's `on("end"` callback, after the entire response has been received. How to create such a promise has been asked before. Storing `grades_array` will need to be moved so it is performed after the `await` operator returns the data needed. – traktor Mar 08 '21 at 02:52
  • Thanks @traktor. I’ll try to figure out. Best. – DiF Mar 08 '21 at 03:12

1 Answers1

0

Almost any time that you see a call to some .on(...), that code isn’t going to run right away, but at some future time.

To make this async, you can wrap the whole thing in a Promise() constructor, and then call resolve() when the thing that you were waiting for has actually happened.

The minimal change to your code for that would be:

let get = new Promise(resolve => {
  https.get(
    {
      hostname: "api.kbb.com.br",
      path: path_get_all_vehicles,
    },
    (getRes) => {
      var reply = "";
      getRes.on("data", (chunk) => {
        reply += chunk;
      });
      getRes.on("end", () => {
        var obj = "";
        var obj = JSON.parse(reply);
        gotCars(obj.Response.VehicleList);
        resolve();
      });
    }
  );
});

Now if you console.log() the value of get, you’ll see that it’s a Promise object. The for loop will still keep running, but now that you have a promise, you can await get on the next line. To be able to use that syntax though, you’ll have to make the containing function async, that is, change (req, res) => … to async (req, res) => ….

Some additional things to worry about later, once you get the basics working:

  • What if the http request fails? Then you’ll want to throw an exception or something. Look up Promise.reject() for that case.
  • Now that the request handler is async, express’s default error-handling stuff won’t work so well. This package might help though I personally haven’t used it.

Those are other issues for other times, but hopefully this helps you get unstuck.

andrewdotn
  • 27,806
  • 6
  • 80
  • 110
  • Thank you. I was using promises incorrectly indeed and your answer helped me reflect and adjust the code - all good then. – DiF May 02 '21 at 04:24