I'm finding myself with some inconvenient to return a certain result inside an async function which has an await request inside. I tried both 3 libraries ("http", "request", "then-request"), and it's always the same.
The main idea is that when the ajax ends, its result should be returned; but instead, it returns undefined (it doesn't respect the async/await).
File: index.server.js
const PyService = require("../../../api/services/PyService/validacionCSV");
module.exports = {
pasajeClientes: async function (matcheos) {
let resultado = await PyService.validate(matcheos);
return resultado;
}
}
File: validacionCSV.js
const request = require('then-request');
module.exports = {
validate: async (matcheos) => {
var response;
await request("GET", `${process.env.API_URL}/validate`, {
json: {
csv: {
clients: "datosPersonas.csv",
products: "movimientos.csv"
},
primary_keys: {
clients: "ID",
products: "ID",
},
branches: {
products: "rama",
},
rules: {
clients: matcheos["clientes"],
products: matcheos["productos"],
}
}
}).done((resultado) => {
let matched = resultado.ok;
let no_relationships = resultado.no_relationships;
let repeated = resultado.repeated;
let total = resultado.total;
let type_errors = resultado.type_errors;
response = {
error: false,
message: "",
errorConTipoDatoClientes: type_errors.clients,
errorConTipoDatoProductos: type_errors.products,
errorConClientesSinProductos: no_relationships.clients,
errorConProductosSinCliente: no_relationships.productos,
errorConClientesRepetidos: repeated.clients,
errorConProductosRepetidos: repeated.products,
cantClientesOk: matched.clients,
cantProductosOk: matched.products,
cantClientesEnArchivo: total.clients,
cantProductosEnArchivo: total.products,
}
if (no_relationships.clients > 0 || no_relationships.products > 0
|| repeated.clients > 0 || repeated.products > 0
|| type_errors.clients > 0 || type_errors.products > 0
) {
response.error = true;
response.message = "Los clientes/productos importados poseen errores."
}
else
response.message = "Los clientes/productos importados no poseen errores."
});
return response;
}
}
You are mixing Promise callbacks with async/await. When working with callbacks you can't define a variable outside and then instantiate within the callback and then try to use it outside the call back again. Read more on Promises.
All I did was return response within the callback function.
Try this
const request = require('then-request');
module.exports = {
validate: async(matcheos) => {
var response;
await request("GET", `${process.env.API_URL}/validate`, {
json: {
csv: {
clients: "datosPersonas.csv",
products: "movimientos.csv"
},
primary_keys: {
clients: "ID",
products: "ID",
},
branches: {
products: "rama",
},
rules: {
clients: matcheos["clientes"],
products: matcheos["productos"],
}
}
}).done((resultado) => {
let matched = resultado.ok;
let no_relationships = resultado.no_relationships;
let repeated = resultado.repeated;
let total = resultado.total;
let type_errors = resultado.type_errors;
response = {
error: false,
message: "",
errorConTipoDatoClientes: type_errors.clients,
errorConTipoDatoProductos: type_errors.products,
errorConClientesSinProductos: no_relationships.clients,
errorConProductosSinCliente: no_relationships.productos,
errorConClientesRepetidos: repeated.clients,
errorConProductosRepetidos: repeated.products,
cantClientesOk: matched.clients,
cantProductosOk: matched.products,
cantClientesEnArchivo: total.clients,
cantProductosEnArchivo: total.products,
}
if (no_relationships.clients > 0 || no_relationships.products > 0 ||
repeated.clients > 0 || repeated.products > 0 ||
type_errors.clients > 0 || type_errors.products > 0
) {
response.error = true;
response.message = "Los clientes/productos importados poseen errores."
} else
response.message = "Los clientes/productos importados no poseen errores."
return response
});
}
}
Related
For some reason I have variables outside of my function and I'm updating that variable in my function but when I call that variable in another function I get a undefined typeError
let bikeShare = []
let stations = []
function startRide(vin) {
bikeShare = bikeShare.map((bike) => {
bike.vin === vin ? { ...bike, checkOut: true } : bike
})
return {}
}
function endRide(vin) {
console.log(bikeShare)
bikeShare = bikeShare.map((bike) => {
bike.vin === vin && bike.checkOut
? { ...bike, checkOut: false, totalRides: bike.totalRides + 1 }
: bike
})
return {}
}
function createBike(color = 'red') {
const vin = bikeShare.length + Date.now();
const payload = { vin, color, checkOut: false, totalRides: 0 }
bikeShare.push(payload);
return payload
}
const bike_1 = createBike('red')
const bike_2 = createBike('blue')
const bike_7 = createBike('green')
startRide(bike_1.vin) // in the startRide function I get an array [undefined, undefined, undefined]
endRide(bike_1.vin)
You are in the startRide() function not returning the result of each assignment in the .map method, so it returns undefined which why you see the array of undefined values.
This should fix it:
let bikeShare = []
let stations = []
function startRide(vin) {
bikeShare = bikeShare.map((bike) => {
return bike.vin === vin ? { ...bike, checkOut: true } : bike
})
return {}
}
function endRide(vin) {
console.log(bikeShare)
bikeShare = bikeShare.map((bike) => {
bike.vin === vin && bike.checkOut
? { ...bike, checkOut: false, totalRides: bike.totalRides + 1 }
: bike
})
return {}
}
function createBike(color = 'red') {
const vin = bikeShare.length + Date.now();
const payload = { vin, color, checkOut: false, totalRides: 0 }
bikeShare.push(payload);
return payload
}
const bike_1 = createBike('red')
const bike_2 = createBike('blue');
const bike_7 = createBike('green');
startRide(bike_1.vin) // in the startRide function I get an array [undefined, undefined, undefined]
endRide(bike_1.vin)
To lift this out of comment, the body of the map argument function in startRide is enclosed in curly braces. You could remove the braces or put return bike inside the braces to stop it returning undefined.
However, setting bike.vin to a bike "payload" object with checkout set to true, leaving bike.checkout set to false, is a bug. One solution might be to use find instead of map:
let bikeShare = []
let stations = []
function startRide(vin, start = true) {
const bike = bikeShare.find(bike=>bike.vin === vin);
if( bike) {
bike.checkOut = start;
}
return bike; // for debugging
}
function endRide(vin) {
return startRide( vin, false);
}
function createBike(color = 'red') {
const vin = bikeShare.length + Date.now();
const payload = { vin, color, checkOut: false, totalRides: 0 }
bikeShare.push(payload);
return payload
}
const bike_1 = createBike('red')
const bike_2 = createBike('blue')
const bike_7 = createBike('green')
console.log( startRide(bike_1.vin));
console.log( endRide(bike_1.vin));
let response = {};
var filters = {
topfeaturedandotherfields: req.body.topfeaturedandotherfields,
};
if (req.body.minprice && req.body.maxprice && req.body.brandName) {
var filters = {
$and: [
{ brandName: { $in: req.body.brandName } },
{ topfeaturedandotherfields: req.body.topfeaturedandotherfields },
{ salePrice: { $gte: req.body.minprice, $lte: req.body.maxprice } },
],
};
var result = await productService.getAllProductofhomepage(
filters,
req.body.ordername,
req.body.orderby
);
} else {
if (req.body.minprice && req.body.maxprice) {
var filters = {
$and: [
{ topfeaturedandotherfields: req.body.topfeaturedandotherfields },
{ salePrice: { $gte: req.body.minprice, $lte: req.body.maxprice } },
],
};
var result = await productService.getAllProductofhomepage(
filters,
req.body.ordername,
req.body.orderby
);
}
if (req.body.brandName) {
var filters = {
$and: [
{ brandName: { $in: req.body.brandName } },
{ topfeaturedandotherfields: req.body.topfeaturedandotherfields },
],
};
var result = await productService.getAllProductofhomepage(
filters,
req.body.ordername,
req.body.orderby
);
}
}
if (req.body.limit == true)
var result = await productService.getAllProductofhomepagewithlimit(filters);
else if (req.body.minprice || req.body.maxprice || req.body.brandName) {
} else {
var result = await productService.getAllProductofhomepage(
filters,
req.body.ordername,
req.body.orderby
);
}
if (result.length > 0) {
response = {
message: "Home page products successfully retrieved",
error: false,
data: result,
};
} else {
response = {
message: "Faild to get products",
error: true,
data: {},
};
}
res.status(200).json(response);
This code is used to filter like to see top feature and bestseller or min and max price and the brand name also in this code sort by order name which could be price or brand name or category also in ascending and descending order so now you can see this code is like if and else but I want to optimize and reduce code
You can make this query quite a lot nicer by just dynamically building the query condition instead of breaking the logic into if/else blocks:
export async function login(req: Request, res: Response): Promise<void> {
const response = {};
let filters = {
topfeaturedandotherfields: req.body.topfeaturedandotherfields,
};
if (req.body.minprice || req.body.maxprice) {
const saleCond = { };
if (req.body.minprice) {
saleCond.$gte = req.body.minprice;
}
if (req.body.maxprice) {
saleCond.$lte = req.body.maxprice;
}
filters.salePrice = saleCond
}
if (req.body.brandName) {
filters.brandName = {$in: req.body.brandName}
}
let result = [];
if (req.body.limit == true) {
result = await productService.getAllProductofhomepagewithlimit(filters)
} else {
result = await productService.getAllProductofhomepage(filters, req.body.ordername, req.body.orderby);
}
res.status(200).json({
message: result.length ? 'Home page products successfully retrieved' : 'Failed to get products',
error: result.length === 0,
data: result,
});
}
Not only is this much clearer we only removed a redundant DB call that was made in the process.
I faced some issues regarding concurrency issue in client side JS. There are two lines of code which sends request using axios. But the following code is run before the callback function for axios request is completed.
Any way to solve this concurrency problem? As far as I know async/await is only used in backend JS like Node.JS
var declaration:
var chartGroundTank = echarts.init(document.getElementById('chart-ground-tank'));
var chartElevatedTank = echarts.init(document.getElementById('chart-elevated-tank'));
var optionChartGroundTank = {
series: {
type: 'liquidFill',
data: [{
name: '',
value: 0,
// waveAnimation: false,
amplitude: '4%',
itemStyle: {
color: '',
},
}]
}
};
var optionChartElevatedTank = {
series: {
type: 'liquidFill',
data: [{
name: "",
value: 0,
itemStyle: {
color: ''
},
// waveAnimation: false,
amplitude: '4%',
}]
}
};
function call:
axios
.get("/api/v1/firstAPI")
.then(function (response) {
const groundTankData = response.data.sensor1;
const elevatedTankData = response.data.sensor2;
setWaterTankColor(groundTankData, optionChartGroundTank, 'gnd'); // the values are
setWaterTankColor(elevatedTankData, optionChartElevatedTank, 'elv'); // set slower
chartGroundTank.setOption(optionChartGroundTank); // this two lines
chartElevatedTank.setOption(optionChartElevatedTank); // will run earlier
....
)};
function definition:
function setWaterTankColor(waterLevel, tankType) {
axios.get("/api/v1/metrics/getData", {
...config,
})
.then((response) => {
var { sensor1Info1, sensor1Info2, sensor2Info1, sensor2Info2 } = response.data;
if (tankType == 'gnd') {
var color = waterLevel < sensor1Info1 / 100 || waterLevel > sensor1Info2 / 100 ? optionChartGroundTank.series.data[0].itemStyle.color = ["red"] : optionChartGroundTank.series.data[0].itemStyle.color = ["#2f529a"];
}
else {
var color = waterLevel < sensor2Info1 / 100 || waterLevel > sensor2Info2 / 100 ? optionChartElevatedTank.series.data[0].itemStyle.color = ["red"] : optionChartElevatedTank.series.data[0].itemStyle.color = ["#2f529a"];
}
});
}
You should return the promise, you have in setWaterTankColor, then you can set the option after the callback completed.
function setWaterTankColor(waterLevel, tankType) {
return axios.get("/api/v1/metrics/getData", {
...config,
})
.then((response) => {
var { sensor1Info1, sensor1Info2, sensor2Info1, sensor2Info2 } = response.data;
if (tankType == 'gnd') {
var color = waterLevel < sensor1Info1 / 100 || waterLevel > sensor1Info2 / 100 ? optionChartGroundTank.series.data[0].itemStyle.color = ["red"] : optionChartGroundTank.series.data[0].itemStyle.color = ["#2f529a"];
}
else {
var color = waterLevel < sensor2Info1 / 100 || waterLevel > sensor2Info2 / 100 ? optionChartElevatedTank.series.data[0].itemStyle.color = ["red"] : optionChartElevatedTank.series.data[0].itemStyle.color = ["#2f529a"];
}
});
}
Then you can use the function like this:
axios
.get("/api/v1/firstAPI")
.then(function (response) {
const groundTankData = response.data.sensor1;
const elevatedTankData = response.data.sensor2;
setWaterTankColor(groundTankData, optionChartGroundTank, 'gnd').then(function() { chartGroundTank.setOption(optionChartGroundTank); });
setWaterTankColor(elevatedTankData, optionChartElevatedTank, 'elv').then(function() { chartElevatedTank.setOption(optionChartElevatedTank); });
....
)};
That two lines of codes run earlier because they are sync functions.
In order to make them run later, you can await for the the function setWaterTankColor first.
You can use async/await in your case. I also add trycatch to catch error from API call.
(async function run() {
try {
const response = await axios.get("/api/v1/firstAPI");
const groundTankData = response.data.sensor1;
const elevatedTankData = response.data.sensor2;
await setWaterTankColor(groundTankData, optionChartGroundTank, "gnd");
await setWaterTankColor(elevatedTankData, optionChartElevatedTank, "elv");
chartGroundTank.setOption(optionChartGroundTank);
chartElevatedTank.setOption(optionChartElevatedTank);
} catch (err) {
console.log(err);
}
})();
async function setWaterTankColor(waterLevel, tankType) {
try {
const response = axios.get("/api/v1/metrics/getData", {
...config,
});
const { sensor1Info1, sensor1Info2, sensor2Info1, sensor2Info2 } =
response.data;
if (tankType == "gnd") {
var color =
waterLevel < sensor1Info1 / 100 || waterLevel > sensor1Info2 / 100
? (optionChartGroundTank.series.data[0].itemStyle.color = ["red"])
: (optionChartGroundTank.series.data[0].itemStyle.color = [
"#2f529a",
]);
} else {
var color =
waterLevel < sensor2Info1 / 100 || waterLevel > sensor2Info2 / 100
? (optionChartElevatedTank.series.data[0].itemStyle.color = ["red"])
: (optionChartElevatedTank.series.data[0].itemStyle.color = [
"#2f529a",
]);
}
} catch (err) {
throw err;
}
}
So in below code if i pass ancillaryProductInd as boolean code works, but when I pass it as a string, it does not work. In my understanding the below code should only work when I pass "false" string value and throw error on boolean. Any idea what is the issue here ?
main.ts
request
var rxInfos = [{
"ancillaryProductInd": "false",
"indexID": "eyJrZXkiOiIEOHdpNUpNWmR3PT0ifQ=="
}]
function subQuestionsHandler(rxInfos, data) {
const subQuestionArray = [];
rxInfos.forEach((rxInfo) => {
const subQuestion = {
question: []
};
if (rxInfo.ancillaryProductInd !== undefined && rxInfo.ancillaryProductInd === "false") {
subQuestion.question = data;
subQuestionArray.push(subQuestion);
}
});
return subQuestionArray;
}
subQuestionsHandler(rxInfos, [{
some data
}]);
Your example code works as expected with a string value "false" and doesnt run the if block when a boolean is used. See my example:
var rxInfos = [
{
ancillaryProductInd: "false",
indexID: "eyJrZXkiOiIEOHdpNUpNWmR3PT0ifQ=="
},
{
ancillaryProductInd: false,
indexID: "eyJrZXkiOiIEOHdpNUpNWmR3PT0ifQ=="
}
];
function subQuestionsHandler(rxInfos, data) {
const subQuestionArray = [];
rxInfos.forEach(rxInfo => {
const subQuestion = {
question: []
};
if (
rxInfo.ancillaryProductInd !== undefined &&
rxInfo.ancillaryProductInd === "false"
) {
console.log("no error");
subQuestion.question = data;
subQuestionArray.push(subQuestion);
} else {
console.log("throw error");
}
});
return subQuestionArray;
}
subQuestionsHandler(rxInfos, [
{
test: ""
}
]);
I'm a bit confused about the asynchous call to the DataBase.
I just want to have a javasctipt adapter class for the calls to the web sql. But I'm not quite sure how to do this. Propably somebody have a good hint for me.
The function OfflneAppDBAdapter.prototype.IsDeviceConfigured() should return true or false depending if there are any items in the Table DeviceConfig.
function OfflneAppDBAdapter() {
self = this;
this.deviceIsConfigured = false;
this.Init = function () {
$data.Entity.extend("$de.offlineapp.DeviceConfig", {
Id: { type: "int", key: true, computed: true },
Name: { type: "string", required: true },
Token: { type: "string" },
Type: { type: "string" }
});
$data.EntityContext.extend("$de.offlineapp.DataContext", {
DeviceConfig: { type: $data.EntitySet, elementType: $de.offlineapp.DeviceConfig }
});
}
self.Init();
$de.offlineapp.context = new $de.offlineapp.DataContext({
name: "webSql", databaseName: "OfflineApp"
});
$de.offlineapp.context.onReady(function () {
});
}
// ************************************************************************
// PUBLIC METHODS -- ANYONE MAY READ/WRITE
// ************************************************************************
OfflneAppDBAdapter.prototype.AddDeviceConfig = function (deviceName, deviceToken, deviceTyp) {
$de.offlineapp.context.onReady(function () {
var promise = $de.offlineapp.context.DeviceConfig.toArray(function (x) {
if (x.length == 0) {
var emp = new $de.offlineapp.DeviceConfig({ Name: deviceName, Token: deviceToken, Type: deviceTyp });
$de.offlineapp.context.DeviceConfig.add(emp);
$de.offlineapp.context.saveChanges();
}
}
)
});
}
OfflneAppDBAdapter.prototype.IsDeviceConfigured = function () {
$de.offlineapp.context.onReady(function () {
var promise = $de.offlineapp.context.DeviceConfig.toArray(function (x) {
if (x.length == 0) {
this.deviceIsConfigured = true;
}
}
)
});
return this.deviceIsConfigured;
}
var myOfflineAppDBAdapter = new OfflneAppDBAdapter();
myOfflineAppDBAdapter.AddDeviceConfig("DeviceName", "Token", "iPad");
console.log(myOfflineAppDBAdapter.IsDeviceConfigured());
As expected the console prints "false". I' aware that the jaydata call works with callbacks and the callbacks are not part of the main class. But there must be a possibility to do so?
I would really apprechiate any help.
Thank you in advance....
Chris
UPDATE:
As you requested the startup code:
function OfflineApplication()
{
self = this;
}
OfflineApplication.prototype.StartApplication = function () {
//Check if online, then sync and
if (navigator && navigator.onLine === true) {
this.IsDeviceConfigured();
}
else {
}
}
///check if the device has a base configuration
OfflineApplication.prototype.IsDeviceConfigured = function () {
myOfflineAppDBAdapter.GetDeviceConfiguration(function (result) {
if (result.length > 0) {
myOfflineAppDBAdapter.deviceIsConfigured = true;
myOfflineApplication.HasDeviceAnApplication();
}
else {
///Get the device base conf from the server.
myOfflineAppSynchronisationAdapter.getDeviceConfigurationByToken(token, myOfflineApplication.HasDeviceAnApplication);
myOfflineAppDBAdapter.deviceIsConfigured = true;
}
});
}
///check if the device has an "application config" in general
OfflineApplication.prototype.HasDeviceAnApplication = function () {
myOfflineAppDBAdapter.GetDeviceAnApplication(function (result) {
if (result.length > 0) {
myOfflineApplication.IsDeviceApplicationVersionLatest(result);
}
else {
myOfflineApplication.GetApplication(false);
}
});
}
///the application config could differ from time to time, so we have to check if a different application should be synct with the device
OfflineApplication.prototype.IsDeviceApplicationVersionLatest = function (result) {
myOfflineAppDBAdapter.DeleteDeviceAnApplication(function () { });
console.log(result);
}
///get the application from the server
OfflineApplication.prototype.GetApplication = function (clearConfig) {
if (clearConfig === true)
{
}
myOfflineAppSynchronisationAdapter.getDeviceApplicationByToken(token, myOfflineApplication.LoadApplication);
}
OfflineApplication.prototype.LoadApplication = function () {
console.log('Now everything is finde and the application gets loaded..');
}
var myOfflineAppDBAdapter = new OfflneAppDBAdapter();
var myOfflineAppSynchronisationAdapter = new OfflineAppSynchronisationAdapter();
var myOfflineApplication = new OfflineApplication();
myOfflineApplication.StartApplication();
There is no sync way. You handling promises wrong. Make your code simple :) You'll need something like this:
$data.Entity.extend("$de.offlineapp.DeviceConfig", {
Id: { type: "int", key: true, computed: true },
Name: { type: "string", required: true },
Token: { type: "string" },
Type: { type: "string" }
});
$data.EntityContext.extend("$de.offlineapp.DataContext", {
DeviceConfig: { type: $data.EntitySet, elementType: $de.offlineapp.DeviceConfig }
});
var context = new $de.offlineapp.DataContext({
name: "webSql", databaseName: "OfflineApp"
});
function AddDeviceConfig(deviceName, deviceToken, deviceTyp) {
return context.DeviceConfig.toArray()
.then(function (x) {
if (x.length == 0) {
var emp = new $de.offlineapp.DeviceConfig({ Name: deviceName, Token: deviceToken, Type: deviceTyp });
context.DeviceConfig.add(emp);
return context.saveChanges();
}
})
}
function IsDeviceConfigured() {
return context.DeviceConfig.toArray()
.then(function (x) {
return x.length > 0;
})
}
context.onReady()
.then(IsDeviceConfigured)
.then(console.log)
.then(function() { return AddDeviceConfig("DeviceName", "Token", "iPad"); })
.then(IsDeviceConfigured)
.then(console.log);
here's a fiddle which does this: http://jsfiddle.net/JayData/cpT5q/1/