I have a function that waits for four external events (I have no control on them. They are randomly received)
function Foo() {
var this.data_1;
var this.data_2;
var this.data_3;
var this.data_4;
}
Foo.prototype.getData = function(){
deviceOne.on('data', (data) => {
this.data_1 = data;
});
deviceTwo.on('data', (data) => {
this.data_2 = data;
});
deviceThree.on('data', (data) => {
this.data_3 = data;
});
deviceFour.on('data', (data) => {
this.data_4 = data;
});
return {
"data_from_device_1": this.data_1,
"data_from_device_2": this.data_2,
"data_from_device_3": this.data_3,
"data_from_device_4": this.data_4
}
};
var foo = new Foo();
console.log(foo.getData()); // {'undefined', 'undefined', 'undefined', 'undefined'}
As you can see, the function won't wait for them and it will return 4 undefined objects. I've been looking for a solution and it seems like using async could help. It's just I didn't understand how to use it correctly in my case
To use async (and it does help here), first you need to do a promise version of your deviceXYZ.on:
const waitForData = device => new Promise((resolve, reject) => {
device.on('data', (data) => {
resolve(data);
});
// ...presumably hook up some kind of error event and use `reject` here...
});
Then (I've converted to class syntax here since if you're using async, you can use the new, simpler notation):
class Foo {
async getData() {
// ^^^^^
const data = await Promise.all([
// ^^^^^
waitForData(deviceOne),
waitForData(deviceTwo),
waitForData(deviceThree),
waitForData(deviceFour)
]);
this.data_1 = data[0];
this.data_2 = data[1];
this.data_3 = data[2];
this.data_4 = data[3];
return {
data_from_device_1: this.data_1,
data_from_device_2: this.data_2,
data_from_device_3: this.data_3,
data_from_device_4: this.data_4
};
}
}
Then code using it also has to be in an async function:
( async () => {
// ^^^^^
try {
const foo = new Foo();
console.log(await foo.getData()); // {'undefined', 'undefined', 'undefined', 'undefined'}
// ---------^
} catch (e) {
// Handle/report error
}
})();
...or it can use the promise directly in a non-async function:
const foo = new Foo();
foo.getData()
.then(result => {
console.log(result);
})
.catch(error => {
// Handle/report error
});
More:
async/await
Promise
Promise.all
class
Arrow functions
Side note: This is invalid syntax:
function Foo() {
var this.data_1; // Error here
var this.data_2;
var this.data_3;
var this.data_4;
}
At the moment, you don't declare properties in JavaScript. However, before too long (possibly ES2019, almost certainly ES2020) class syntax will be extended by the class fields proposal, currently at Stage 3 in the proposals process. When that happens, you can declare the "shape" of objects created by the Foo class (which can mean the objects go through fewer shape changes, which helps improve performance; it's also useful documentation for people reading the code). That would change Foo to look like this:
class Foo {
data_1; // These are field (property) declarations
data_2;
data_3;
data_4;
async getData() {
// ...
}
}
Related
Looks nobody on internet describes similar problem, and similar solution is not working for me.
I am trying to scrape webpage so I created class for parser and one of the method looks like follows:
get chListUrl() {
return "http://www.teleman.pl/program-tv/stacje";
}
getChannels() {
var dict = {};
axios.get(this.chListUrl).then(function (response) {
var $ = cheerio.load(response.data);
var ile_jest_stacji = $('#stations-index a').length;
$('#stations-index a').each( (i,elem) => {
let href = $(elem).attr('href');
let kodstacji = href.replace(/\/program-tv\/stacje\//ig,'');
let nazwastacji = $(elem).text();
dict[nazwastacji]=kodstacji;
});
return dict;
}).catch(function (error) {
console.log(error);
return null;
}).finally(function() {
console.log("Koniec");
});
}
And problem is getChannels must be indirectly asynchronous because it contains axios BUT
let tm = new TM();
var a = tm.getChannels();
a is always undefined and it should be dictionary! Such construct means "assing to variable a result of execution of tm.getChannels()" so assignment should always be done AFTER whole function ends. Otherwise such syntax in language is useless because you will never be sure what value is stored in variable, and such errors are difficult to find and debug.
var a = await tm.getChannels();
NOT WORKING -> SyntaxError: await is only valid in async function (huh?)
adding async to getChannels() changes nothing.
Assing async to getChannels() and remove 'await' from assignment returns Promise{undefined} (huh?)
putting async before axios changes nothing as response is already handled by .then()
changing return dict to return await dict gives another "await is only valid in async function" (huh? axios is asynchronous)
I'm scratching my head over this for 2 weeks.
In Swift when something is return in completion handler it is assigned to variable in proper moment, why something returned by Promise not works the same way?
You need to be inside an async function to use the await expression:
The await operator is used to wait for a Promise. It can only be used inside an async function.
await operator on MDN
Example sourced from MDN:
async function f1() {
var x = await resolveAfter2Seconds(10);
console.log(x); // 10
}
f1();
Fixing your issue
class TM {
get chListUrl() {
return "http://www.teleman.pl/program-tv/stacje";
}
async getChannels() { // you need not use get syntax
let dict = {};
try { // we will be handling possible errors with try catch instead of reject
const response = await axios.get(this.chListUrl);
let $ = cheerio.load(response.data);
let ile_jest_stacji = $('#stations-index a').length;
$('#stations-index a').each( (i,elem) => {
let href = $(elem).attr('href');
let kodstacji = href.replace(/\/program-tv\/stacje\//ig,'');
let nazwastacji = $(elem).text();
dict[nazwastacji]=kodstacji;
});
return dict;
} catch(ex) {
console.log(ex);
return null
}
}
}
// let's make it work!
(async function() {
const tm = new TM()
const channels = await tm.getChannels()
// do whatever you want with channels
console.log(channels)
})()
Now, you're probably not going to call getChannels out of nowhere like this instead you will probably be inside a function that you yourself defined, you need to add the async keyword to this function. Whatever block function your code is in needs to be async.
If you want to use the async/await syntax you should remove the .then() syntax and you can resolve that way:
async getChannels() {
const response = await axios.get(this.chListUrl);
return response
}
You can learn more about async/await in the link: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function
Let's say I have this code:
const myFunction = async => {
const result = await foobar()
}
const foobar = async () => {
const result = {}
result.foo = await foo()
result.bar = await bar()
return result
}
And I want this:
const myFunction = () => {
const result = foobar()
}
I tried to wrap foobar like this:
const foobar = async () => {
return (async () => {
const result = {}
result.foo = await foo()
result.bar = await bar()
return result
})()
}
But this still return a promise
I can't use .then in myFunction, I need that foobar returns the result variable instead a promise.
The problem is that myFunction is an async function and it will return a promise but It should return undefine I need to get rid of async in myFunction.
Edit: as Sebastian Speitel said, I want to convert myFunction to sync
Edit 2: to Shilly, I am using nightwatch for end2end test, nightwatch will call myFunction() if there are no errors in the execution of the function it will run perfectly, if there's an error then nightwatch's virtual machines will run forever instead stop, this problem happens if the called function is async.
To change an asynchronous function into a normal synchronous function you simply have to drop the async keyword and as a result all await keywords within that function.
const myFunction = async () => {
const result = await foobar();
// ...
return 'value';
};
// becomes
const myFunction = () => {
const result = foobar();
// ...
return 'value';
};
You should however keep one simple rule in mind.
You can't change a asynchronous function into a synchronous function if the return value depends on the value(s) of the resolved promise(s).
This means that functions that handle promises inside their body, but from whom the return value doesn't depend on those resolved promises are perfectly fine as synchronous functions. In most other scenarios you can't drop the asynchronous behaviour.
The following code gives you an example for your situation, assuming the return value of myFunction doesn't depend on the resolved promise.
const myFunction = () => {
const result = foobar();
result.then(data => doSomethingElse(data))
.catch(error => console.error(error));
return 'some value not dependent on the promise result';
};
If you want to learn more about promises I suggest checking out the promises guide and the async/await page.
Have you looked into using .executeAsync() and then having the promise call the .done() callback? That way it should be possible to wrap foobar and just keep either the async or any .then() calls inside that wrapper.
My nightwatch knowledge is very stale, but maybe something like:
() => {
client.executeAsync(( data, done ) => {
const result = await foobar();
done( result );
});
};
or:
() => {
client.executeAsync(( data, done ) => foobar().then( result => done( result )));
};
Any function marked with async will return a Promise. This:
const foobar = async () => {
return 7;
}
Will a return a Promise of 7. This is completely independent of wether the function that calls foobar is async or not, or uses await or not when calling it.
So, you're problem is not (only) with myFunction: is foobar using async which forces it to always return a Promise.
Now, said that, you probably can't achieve what you want. Async-Await is only syntax sugar for promises. What you're trying is to return a synchronous value from an asynchronous operation, and this is basically forbidden in javascript.
You're missing very important understanding here between the synchronous and asynchronous nature of the code.
Not every async function can be converted to synchronous function. You can use callback pattern instead of await/async, but I doubt that would be useful to you.
Instead I would recommend you just use await, as in your first code example, and leave functions as async, it shouldn't harm your logic.
Check this out
function foo(){
return 'foo'
}
function bar(){
return 'bar'
}
const foobar = () => {
return new Promise((resolve)=>{
let result = {}
result.foo = foo()
result.bar = bar()
return resolve(result)
})
}
const myFunction = () => {
const result = foobar()
let response = {}
result.then(val=>{
response = Object.assign({}, val);
return response
});
}
var test = myFunction()
console.log(test)
This question already has an answer here:
Asynchronous operations in constructor
(1 answer)
Closed 4 years ago.
I am working on a project in Node - a language with which I have little familiarity.
In the project, I have a class that will be responsible for reading and writing data to a database - in this case, LevelDB. Ideally, I'd like to set-up the database connection in the constructor synchronously so that methods (writeItem, readItem, etc.) don't fail if they're called too fast. Or in other words, I don't want the constructor to return and allow the next line of code to run until all the promises are fulfilled.
I think I am either missing something fundamental to the language or there is some design pattern in node that I'm not aware of. A toy example that fails in the same way is here:
class AClass {
constructor(n) {
this.name = n;
console.log('calling init.');
this.init();
console.log('init returned.');
}
func() {
return new Promise(resolve => {
setTimeout(() => {
resolve(true);
}, 2000);
});
}
async init() {
console.log('calling func()');
let x = await this.func();
console.log('after func(): ');
}
}
let x = new AClass('test');
console.log(JSON.stringify(x));
This produces the output:
calling init.
calling func()
init returned.
{"name":"test"}
after func():
This is surprising to me. I would have expected:
calling init.
calling func()
after func():
init returned.
{"name":"test"}
The final objective is to instantiate a class that connects to a levelDB instance and does not return the object until that connection is made. So the code might look something like this:
let storage = new StorageObject();
storage.addItem(key, value); // <-- this fails unless StorageObject
// finishes the db setup.
Thanks!
Sam
Your objective of not returning the instance until after the connection won't work (at least not like this). The constructor's job is to create an instance and return that. Functions need to return something synchronously. If it's performing some async operation then a function can return a promise instead, but you don't want a promise from the constructor — you need the instance.
The easy way to do this is to require your object to be initialized after it's created, then the constructor can construct and return the instance and the init function is free to return a promise:
class AClass {
constructor(n) {/* some setup */}
func() {
return new Promise(resolve => {
setTimeout(() => {
resolve("some name");
}, 1000);
});
}
async init() {
this.name = await this.func();
return this
}
}
new AClass('test').init()
.then((initialized_obj) => console.log(initialized_obj))
If you're doing this in node, you could also use eventEmitters to emit an event when the instance has been initialized. Something like:
const EventEmitter = require('events');
class AClass extends EventEmitter{
constructor(n) {
super()
this.init()
.then(() => this.emit("initialized"))
}
func() {
return new Promise(resolve => {
setTimeout(() => {
resolve("some name");
}, 1000);
});
}
async init() {
this.name = await this.func();
return this
}
}
let x = new AClass('test')
x.on('initialized', () => console.log(x.name))
Two things about your code:
When you call an async function (this.init()), it will be executed uptill its returned await statement and will return a promise and the control will go to next line (console.log('init returned.')). Understanding this will resolve your confusion.
Code after await statement (console.log('after func(): ');) will execute only after awaited promise has been resolved.
I have repurposed your code to do what you want.
async function AClass(n) {
let obj = {}
obj.func = () => {
return new Promise(resolve => {
setTimeout(() => {
resolve(true);
}, 2000);
});
};
obj.init = async function () {
console.log('calling func()');
let x = await obj.func();
console.log('after func(): ');
};
obj.name = n;
console.log('calling init.');
await obj.init();
console.log('init returned.');
return obj;
}
let x = AClass('test');
x.then((resolveValue) => {
/*
*Now the "class" has been instantiated,
* code to use the object of the "class" goes here
* */
console.log(JSON.stringify(resolveValue));
});
If you really want the constructor to return a working AClass synchronously, you can do it by rewriting other parts of code a little. I'm assuming that the methods writeItem, readItem, etc are asynchronous. All you have to do is rewrite these methods so that they wait for the object to be initialised (if necessary) before proceeding.
For example, suppose your class looks like this:
class AClass {
constructor(n) {
this.name = n;
this.init();
}
async init() {
...
}
async writeItem(item) {
return await db.writeItem(item);
}
async readItem(itemId) {
return await db.readItem(itemId);
}
}
You should then be able to rewrite it as follows:
class AClass {
constructor(n) {
this.name = n;
this.awaitInit = this.init();
}
async init() {
...
}
async writeItem(item) {
await this.awaitInit;
return await db.writeItem(item);
}
async readItem(itemId) {
await this.awaitInit;
return await db.readItem(itemId);
}
}
Because I'm using a prototype that has functions calling other functions in the same prototype I have to refer to that method using this
The Problem this Created:
But because of that, I have to preserve a context to use this that has me forming very ugly .bind(this) walls.
Here is a simplified example I made for laughs.
Killmyself.prototype.fireLeMissles = function () {
return new Promise(function(resolve,reject) {
this.anotherFunction(param).then(function(result) {
someList.forEach(function(item) {
this.fireLeMissles().then(function(anotherResult){
promiseList.push(anotherResult)
})
},this);
Promise.all(promiseList).then(function(promiseItem){
childPlacesIds.forEach(function(childPlaceId) {
//Do Other Stuff
},this);
});
resolve(result);
}.bind(this).catch(function(err){
console.log("Yea, life sucks sometimes.")
}));
}.bind(this));
}
Killmyself.prototype.another = function(){
//Other stuff
}
You can see because of calls to functions in the same prototype such as this.anotherFunction(... and this.fireLeMissles(... I had to do deep preservation of context,which now (in my much larger version of this) is making this code hard to work with.
Question:
Is this a "man up and get use to the harder aspects of JavaScript" thing - or do you seasoned developers see simple ways that deep binding like this could have been avoided?
If you are using ES6, you can benefit from arrow functions, which preserve the context.
var counter = function () {
this.count = 0;
setInterval( () => { // arrow function
console.log(this.count++); // context is preserved
}, 1000)
}
var counter = new counter();
So, your code would become something like:
Killmyself.prototype.fireLeMissles = function() {
return new Promise((resolve, reject) => {
this.anotherFunction(param).then(result => {
someList.forEach(item => {
this.fireLeMissles().then(anotherResult => {
promiseList.push(anotherResult)
});
});
Promise.all(promiseList).then(promiseItem => {
childPlacesIds.forEach(childPlaceId => {
//Do Other Stuff
});
});
resolve(result);
}).catch(err => {
console.log("Yea, life sucks sometimes.")
});
});
}
For ES5, you can just either use .bind just like the way you did or you can assign this to something else in the function with the desired context, then use that variable inside the inner functions.
Killmyself.prototype.fireLeMissles = function() {
var self = this; /// use `self` instead of `this` from now on.
return new Promise(function(resolve, reject) {
self.anotherFunction(param).then(function(result) {
someList.forEach(function(item) {
self.fireLeMissles().then(function(anotherResult) {
promiseList.push(anotherResult)
})
});
Promise.all(promiseList).then(function(promiseItem) {
childPlacesIds.forEach(function(childPlaceId) {
//Do Other Stuff
});
});
resolve(result);
}).catch(function(err) {
console.log("Yea, life sucks sometimes.")
});
});
}
for starters I do not understand you you need a new Promise.. here, like #loganfsmyth said, I would simply use arrow functions and reduce the complexity:
Killmyself.prototype.fireLeMissles = function (param) {
return this.anotherFunction(param)
.then(someList => {
var promiseList = someList.map( item => this.fireLeMissles(item));
return Promise.all(promiseList);
}).then(childPlacesIds => {
childPlacesIds.forEach(childPlacesId = {
// .... do something;
});
// return something.
}).catch(err => console.log("Yea, life sucks sometimes."));
}
P. S: I am not sure where this param, someList, childPlacesIds is coming from, and assumed that you are initializing that promiseList as empty array.
Mido's answer is good, I just wanted to provide an alternative take on it which I think would be useful to know - using promises for the proxies they are:
Killmyself.prototype.fireLeMissles = function () {
let fn = this.anotherFunction(param);
let others = fn.then(_ => someList.map(this.fireLeMissles, this));
let othersP = Promise.all(others);
othersP.then(/* do OtherStuff */);
return othersP; // or whatever its then returned
}
Of course, this gets even easier with a library like bluebird.
I have a nodejs library written using the async/await module. I try to consume it from a library which uses regular callbacks. Here is a sample code:
var async = require('asyncawait/async');
var await = require('asyncawait/await');
var Promise = require('bluebird');
var foo = async (function() {
var resultA = await (Promise.promisify(bar));
return 111;
})
function bar(callback) {
setTimeout(callback, 2000)
}
function moo() {
var f = async.cps(foo)
f(function(err, res) {
console.log(res)
})
}
moo()
I expected console.log to print 111 but instead it prints:
{ _bitField: 0,
_fulfillmentHandler0: undefined,
_rejectionHandler0: undefined,
_progressHandler0: undefined,
_promise0: undefined,
_receiver0: undefined,
_settledValue: undefined }
btw if I inline the foo implementation in the "async.cps" line it works (but this is not an option since its an external library).
Any idea?
I've looked at the library your using, and by the look of it (there isn't much, and certainly no working samples), you're not using it correctly.
async(fn) will return a function that accepts some input values and upon execution will return a promise (Probably standard Promises/A+). Inside, it will call fn with the input parameters, and will resolve the returned promise when fn has returned.
async.cps(...) will return a function that accepts some input values and a node style callback function (function (err, res)) and upon execution will return a void (undefined). Inside, it will call fn with the input parameters, and will call the callback function when fn has returned with the appropriate values.
what your code does is create a function with async(fn), then pass this function to async.cps(..) as if you called async.cps(async(fn)), but that doesn't make any sense.
What you could do if you wanted to "convert" a promise to a node style callback function (unpromisifying!) using this library is this:
function moo() {
var f = async.cps(function() {
return await (foo())
})
f(function(err, res) {
console.log(res)
})
}
You double-asynced the foo function. Here are your options, depending on whether you can modify the declaration of foo or not:
Leave foo async, and create a function f that accepts a node-style callback using cps:
var async = require('asyncawait/async');
var await = require('asyncawait/await');
var Promise = require('bluebird');
var foo = async(function() {
var resultA = await (Promise.promisify(bar));
return 111;
});
function bar(callback) {
setTimeout(callback, 1000);
}
function moo() {
var f = async.cps(function() {
return await(foo());
});
f(function(err, res) {
console.log(res);
});
}
moo();
Leave foo async, and use an async moo:
var async = require('asyncawait/async');
var await = require('asyncawait/await');
var Promise = require('bluebird');
var foo = async(function() {
var resultA = await (Promise.promisify(bar));
return 111;
});
function bar(callback) {
setTimeout(callback, 1000);
}
var moo = async(function() {
try {
var res = await(foo());
console.log(res);
} catch (err) {
}
});
moo();
Make foo already cps at the declaration:
var async = require('asyncawait/async');
var await = require('asyncawait/await');
var Promise = require('bluebird');
var foo = async.cps(function() {
var resultA = await (Promise.promisify(bar));
return 111;
});
function bar(callback) {
setTimeout(callback, 1000);
}
function moo() {
foo(function(err, res) {
console.log(res);
});
}
moo();