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 :)
Related
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);
}
})();
I wish to be able to return and end a function from inside a function inside that function. Just typing return inside the function will return for that function instead of the one I'm trying to return for.
function iWannaEscapeThis() {
function someSideFunction() {
//need to return iWannaEscapeThis here somehow
}
}
You need to call it main function and return it
function iwannaescapethis() {
function somesidefunction() {
return 'from inside'
}
return somesidefunction();
}
console.log(iwannaescapethis())
Async function
async function iwannaescapethis() {
async function somesidefunction() {
let promise = new Promise((resolve, reject) => {
setTimeout(() => resolve("from inner"), 2000)
});
return await promise;
}
let x = await somesidefunction()
return x;
}
(async function(){
let res = await iwannaescapethis()
console.log(res)
})()
Return the nested function:
function iwannaescapethis() {
function somesidefunction() {
return "sideFunction";
}
return somesidefunction();
}
console.log(iwannaescapethis());
It's possible to use exceptions for doing this, but a big warning it's not advised you use exceptions for flow control too often.
But if you really do need this feature, here is an example.
class ReturnException extends Error {
constructor (value) { super(); this.value = value; }
};
function thisIsTheOuterFunction() {
try {
function someSubFunction() {
function someOtherSubFunction() {
throw new ReturnException("The answer is 42");
}
someOtherSubFunction();
}
someSubFunction();
} catch (e) {
if (e instanceof ReturnException) {
return e.value;
} else throw e;
}
}
console.log(thisIsTheOuterFunction());
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
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..
How do I clean up after a generating function has been exited. The problem I am having is I made a little utility to read-lines from a file using Async Iterators a stage 3 EcmaScript proposal and I want it to close the file it's reading after I exit a for-of loop. Currently since this feature is only stage 3, in order to get this to run you'll have to use babel to transpile it.
With the below code you can see the problem. If you pipe in an input file then it will read one line and print that the line reader is open still.
I would like to explicitly close the file inside the LineReader class when it's iterator is returned.
I know I can do this by not using a generating function but instead return an iterator object as outlined here, but is there any way I can keep the generating function and define a return method for it.
src/line-reader.js
function deferred() {
const def = {}
def.promise = new Promise((resolve, reject) => {
def.resolve = resolve
def.reject = reject
})
return def
}
/**
* PromiseQueue from GTOR adapted for ES2015
* https://github.com/kriskowal/gtor
*/
class PromiseQueue {
constructor (values) {
this.ends = deferred();
if (values) {
values.forEach(this.put, this);
}
}
put(value) {
const next = deferred();
this.ends.resolve({
head: value,
tail: next.promise
});
this.ends.resolve = next.resolve;
}
get () {
var result = this.ends.promise.then(node=>node.head);
this.ends.promise = this.ends.promise.then(node=>node.tail)
return result;
};
}
class LineReader {
constructor (input, output) {
this.lineReader = require('readline').createInterface({ input, output });
this.lineQueue = new PromiseQueue();
this.isClosed = false;
this.lineReader.on('line', (line) => {
this.lineQueue.put(line);
this.lineReader.pause();
});
this.lineReader.on('close', (line) => {
this.isClosed = true;
this.lineQueue.put(Promise.resolve({done: true}));
});
this.lineReader.on('SIGCONT', () => {
// `prompt` will automatically resume the stream
this.lineReader.prompt();
});
}
readLine(){
var result = this.lineQueue.get().then(function (data) {
if(data && data.done) {
throw new Error("EOF");
}
return data
});
this.lineReader.resume();
return result;
}
close () {
this.lineReader.close();
}
async * [Symbol.asyncIterator] () {
try {
while(!this.isClosed) {
yield await this.readLine();
}
} catch (e) {
this.close();
}
}
}
module.exports = LineReader;
test/test.js
var LineReader = require("../src/line-reader");
var lineReader = new LineReader(process.stdin);
(async ()=> {
var i = 1;
for await (var line of lineReader) {
console.log(`${i++} ${line}`);
break;
}
console.log(`The line-reader is ${lineReader.isClosed ? "closed" : "open" }.`);
lineReader.close();
})().catch(e=> {
console.error(e)
})
You can always Object.assign over a called generating function and extend its returned iterator with your return method.
Like so:
class LineReader {
// ... elided code ...
async * iterate() {
try {
while(!this.isClosed) {
yield await this.readLine();
}
} catch (e) {
this.close();
}
}
[Symbol.asyncIterator] () {
return Object.assign(this.iterate(), {
return: () => {
this.close();
}
});
}
}
Or optionally you can do it this way, but I prefer the first as it doesn't create a new function each time it's called and the first looks much better to me.
class LineReader {
// ... elided code ...
[Symbol.asyncIterator] () {
var that = this;
return Object.assign(async function * iterate() {
try {
while(!that.isClosed) {
yield await that.readLine();
}
} catch (e) {
that.close();
}
}(), {
return: () => {
this.close();
}
});
}
}
Just add a finally to your try block. finally will execute even if the function has returned (which it returns when someone breaks out of a for of loop). This guarantees that you function will clean up and you don't have to modify your function much. I just found this out thanks to this article by Jake Archibald.
class LineReader {
// ... elided code ...
async * [Symbol.asyncIterator] () {
try {
while(!this.isClosed) {
yield await this.readLine();
}
} finally {
this.close();
}
}
}