Update object after function return - javascript

I am trying to create a simple Api consume service with JavaScript. This service has a 3 properties (loading, data, error) and I want to change thats properties values dynamicly in promise and I want to use this service like that ->
const { loading, data, err } = new ApiService().send(request).getResults();
My main goal is dynamic loading and data so during Api call loading property's value equals to true and when Api call finished loading property's value equals to false and data property filled with Api response. So I want to use this service like that ->
const { loading, data, err } = new ApiService().setSync(false).send(q).getResults();
if (loading) {
pElement.text("Loading");
} else {
pElement.text(data.id);
}
My Api service codes:
function ApiService(header = {}) {
this._loading = false;
this._data = {};
this._header = header;
this._error = "";
this._isAsync = false;
}
ApiService.prototype.setSync = function (isAsync = false) {
this._isAsync = isAsync;
return this;
};
ApiService.prototype.send = function (request) {
const self = this;
if (!this._isAsync) {
this._loading = true;
request
.then(function (data) {
self._loading = false;
self._data = data;
})
.catch(function (e) {
self._error = e;
});
return this;
} else {
return request
.then(function (data) {
self._loading = false;
self._data = data;
return data;
})
.catch(function (e) {
self._error = e;
});
}
};
ApiService.prototype.getResults = function () {
const self = this;
return { loading: self._loading, data: self._data, err: self._error };
};
module.exports = ApiService;
This service works once and its returns default values as normaly but I want to dynamic update values. Is it possible?

Short answer: No. After a return statement you cant update the values like you have in mind.
Frameworks like React.js do have syntax like this in their Hooks API, but they also have components which are executed again every time a state changes, resulting in having the variables re-evaluated.
In Vanilla JS land the easiest thing to do is to return a Promise, wait for it to finish and then continue. I think that using async / await is the closest thing to making your code behaving as you would like.
The snippet below demonstrates how that would look and run. The returned loading value would always be false as you would only get the result after it's done loading, so it can be omitted.
function ApiService(header = {}) {
this._loading = false;
this._data = {};
this._header = header;
this._error = "";
this._isAsync = false;
}
ApiService.prototype.send = async function(request) {
this._loading = true;
this._data = {};
this._error = "";
try {
const data = await request();
this._data = data;
} catch(error) {
this._error = error;
} finally {
this._loading = false;
return {
data: this._data,
error: this._error,
};
}
}
(async () => {
pElement.text("Loading");
const { data, err } = await new ApiService().send(request);
if (!err) {
pElement.text(data.id);
}
})();

Related

Protractor JavaScipt function call returns null

I'm trying to call my function from it() but I always get return value of null. I thought my function assigns the value to the return variable back to the caller.
file: "helperDropDownBx.js"
module.exports = function() {
function myFunctionTest() {
var reTxt = null;
try {
browser.wait(EC.visibilityOf(id_dropDownValue), 30000);
id_dropDownValue.getAttribute("value").then(function(text) {
retTxt = text;
});
} catch(err) {
throw new Error(err.message);
}
return retTxt;
}
return{
myFunctionTest : myFunctionTest
}
}
file: "TestHelpers.js"
const myHelper = require("../pages/helpers/helperDropDownBx.js");
describe("[Test Helpers]", function(){
var myHelperObj = new myHelper();
it('testing Helpers', function() {
try{
//attempt#1, but not working
var retVal = myHelperObj.myFunctionTest();
retVal.then(function (value){
console.log(value);
)};
//attempt#2, but not working
myHelperObj.myFunctionTest().then(function(value){
console.log(value);
)};
}catch(err){
throw new Error(err.message);
}
});
});
both of my attempts above, always return null
file: "helperDropDownBx.js"
module.exports = {
myFunctionTest: async function () {
// this is a bad practice to start a function with wait
// all wait should be handled after you complete an action and not before
await browser.wait(EC.visibilityOf(id_dropDownValue), 30000);
return id_dropDownValue.getAttribute("value")
}
}
file: "TestHelpers.js"
const myHelper = require("../pages/helpers/helperDropDownBx.js");
describe("[Test Helpers]", function(){
it('testing Helpers', async function() {
var value = await myHelper.myFunctionTest();
console.log(value)
});
});
if you still curious what you needed to do with .then() to make it work, then something like this
module.exports = {
myFunctionTest: function() {
return browser.wait(EC.visibilityOf(id_dropDownValue), 30000)
.then(() => {
return id_dropDownValue.getAttribute("value")
.then(function(text) {
return text;
})
})
}
}
Looks like hell to me :)

Javascript reduce in other function

How can I get the data I parse from my JSON file to run through the reduce function to eliminate duplicates and then beeing available by calling the getFiilteredData() function?
async function getFilteredData() {
return new Promise((resolve) => {
oWebViewInterface.on("loadData", function (data) {
var schwellWerte = data.monitor;
var monitorData = data.data.reduce((arr, d) => {
if (arr.find((i) => i.zeitstempel === d.zeitstempel)) {
return arr;
} else {
return [...arr, d];
}
}, []);
resolve(monitorData); // resolve the promise with the data
//can I do: resolve(monitorData, schwellWerte) to resolve both?
});
});
}
Doing it like this, results in "Uncaught TypeError: Cannot read property '0' of undefined" for the two last console.log() but the first works fine and logs the expected value.
The easiest way is to use a Promise and async/await. Wrap your asynchronous call in a Promise and await it at the client:
async function getFilteredData() {
return new Promise( resolve => {
oWebViewInterface.on("loadData", function (data) {
var monitorData = JSON.parse(data).reduce((arr, d) => {
if (arr.find((i) => i.zeitstempel === d.zeitstempel)) {
return arr;
} else {
return [...arr, d];
}
}, []);
resolve(monitorData); // resolve the promise with the data
});
});
}
and then when you call it just await the call
var filteredData = await getFilteredData();
console.log(filteredData[0].id);
Edit: I notice from your comments that in your code you're calling getFilteredData twice - this seems like a bad idea. Call it once. If you put the configuration of your chart into its own async method this gets easier
async function configChart(){
var data = await getFilteredData();
var werteArr = [];
var zsArr = [];
for (i = 0; i < data.length; i++) {
werteArr.push(data[i].wert);
zsArr.push(data[i].zeitstempel);
}
//defining config for chart.js
var config = {
type: "line",
data: {
labels: zsArr ,
datasets: {
data: werteArr,
// backgroundcolor: rgba(182,192,15,1),
},
},
// -- snip rest of config -- //
}
var ctx = document.getElementById("canvas").getContext("2d");
window.line_chart = new window.Chart(ctx, config);
}
window.onload = function () {
configChart(); // no need to await this. It'll happen asynchronously
};

react-native dynamically get/set configuration variables (API URL)

My app starts by choosing a country and I want to change my API_URL according to the country data a user selects.
My country_id is stored in AsyncStorage. I have tried this but is has not worked.
function Configure() {
let url = '';
}
Configure.prototype.getApiUrl = function (params = null) {
AsyncStorage.getItem("country").then((value) => {
if(value == 223) {
return "https://www.website.com/usa_api"
}else{
return "https://www.website.com/api"
}
});
}
module.exports = Configure
You have to return the Promise function
function Configure() {
let url = '';
}
Configure.prototype.getApiUrl = function (params = null) {
return AsyncStorage // Have to return the promise
.getItem("country")
.then((value) => {
if (value == 223) {
return "https://www.website.com/usa_api"
} else {
return "https://www.website.com/api"
}
});
}
module.exports = Configure
Usage
Now that we are returning the Promise, we can await it from where you want to use it
// Use the promise
Configure
.getApiUrl()
.then((apiURL) => {
// You should be getting the API URL here
})
// Or better looking code with async/await
const apiURL = await Configure.getApiUrl();
// You should be getting the API URL here

Issue setting callback results equal to object properties (node-tesseract)

I'm trying to use the results of a callback function as properties of an object. Here is how I am trying to use the module I am building:
var Screenshot = require("./Screenshot.js")
const test = async function() {
let screenshot = new Screenshot("./screenshots/Screenshot_20180806093446.jpg")
await screenshot.readScreenshot()
console.log(screenshot.text)
}
test() // logs nothing to console
readScreenshot() doesn't seem to be doing its job. I'm sure there's a finer detail I am missing to making this work.
./Screenshot.js code below:
var tesseract = require("node-tesseract");
module.exports = class Screenshot {
constructor(path) {
this.path = path;
this.readScreenshot = this.readScreenshot.bind(this);
}
readScreenshot() {
tesseract.process(this.path, (err, text) => {
if (err) {
throw err;
} else {
this.text = text.split("\n").filter(el => el.trim() !== "");
}
});
}
};
It would also be nice if I can get the constructor of class Screenshot to properly call this function so I do not have to do it manually in app.js
You will have to change the code so that readScreenshot method allows you to return a promise object.
readScreenshot() {
return new Promise((resolve, reject) => {
tesseract.process(this.path, (err, text) => {
if (err) {
reject(err);
} else {
resolve(text.split("\n").filter(el => el.trim() !== ""))
}
});
});
}
//
var Screenshot = require("./Screenshot.js")
const test = async function() {
let screenshot = new Screenshot("./screenshots/Screenshot_20180806093446.jpg")
let resp = await screenshot.readScreenshot()
console.log(resp)
}
test() // logs nothing to console

Return two different things inside same return in JavaScript/AngularJS

There is a service which gets the data and has a then-catch structure:
getData(siteId) {
const accessToken = JSON.parse(sessionStorage.getItem('ls.authorizationData')).token;
const config = { ...
};
this.canceler.resolve();
this.canceler = this.$q.defer();
config.timeout = this.canceler.promise;
const siteIds = this.MyService.getSitesIds();
return this.$http.get(`${TEST_API_URL}value?siteIds=${siteId || siteIds}`, config)
.then(result => {
return {
data: result.data,
};
}).catch((e) => {
if (e.status === -1 && e.xhrStatus === "abort") {
return Promise.resolve({canceled: true});
}
debugger;
this.hasData = false;
return Promise.reject('Cannot access data ');
});
}
I'm calling this function from the controller, initially in $onInit() like this:
$onInit() {
getData.call(null, this);
}
and as a function:
function getData(MyCtrl) {
MyCtrl.loading = true;
MyCtrl.MyService.getData(MyCtrl.siteId).then(result => {
if (result.canceled) {
return;
}
...
}
This works fine.
The problem appears when I want to send a variable from service to controller if data is not there (if the catch() happens).
I tried to change the return by wrapping the Promise inside an object like
return {promise: Promise.reject('Cannot access data ')};
and add the variable inside this object:
return {promise: Promise.reject('Cannot access data ')
hasData: false};
but it seems that it's not the right way. I need to know in the controller if the data was got or not.
Do you have any suggestions how to solve this?
Normally when you want to return more data from a Promise rejection, you do it the same way you would return from a normal exception, you extend from the Error object.
Here is an example..
class MyError extends Error {
constructor (message, extra) {
super(message);
this.extra = extra;
}
}
//standard promise rejection way.
function badPromise() {
return Promise.reject(
new MyError("Bad Error", "Extra Stuff")
);
}
//works if throw error inside an async function
async function badPromise2() {
throw new MyError("Bad Error2", "Extra Stuff2");
}
async function test() {
try {
if (Math.random() > 0.5) await badPromise();
else await badPromise2();
} catch(e) {
if (e instanceof MyError) {
console.error(`${e.message}, extra = ${e.extra}`);
} else {
console.error(`${e.toString()}`);
}
}
}
test();
ps. This is using new ESNext features, but the same applies if doing ES5..

Categories