My class looks like this:
class Test {
constructor() {
}
*test() {
console.log('test');
let result = yield this.something();
return result;
}
something() {
console.log('something');
return new Promise((resolve, reject) => {
resolve(2);
});
}
}
But when I create an object from Test and call the test() method, I don't get the expected result ...
let test = new Test();
console.log(test.test()); // {}
Thought it would return 2.
Logs aren't shown as well.
What am I doing wrong here?
It works properly. You need to call next() on returned value by test method.
let test = new Test();
console.log(test.test().next());
Output
test
something
{ value: Promise { 2 }, done: false }
By calling test.test() you are creating new generator instance. Then you should call next() function on created instance to make generator yield value.
Related
I want to test a class, which contains a private function (Bar), which is called from setInterval. This private function is async and requires a sleep function.
Class I want to test
export class Foo
{
constructor ()
{
setInterval(async () => await this.Bar(), 1000);
}
private async Bar()
{
this.A();
await this.sleep(5000);
this.B();
}
private A()
{
console.log("A");
}
private B()
{
console.log("B");
}
public async sleep(ms: number)
{
return new Promise(resolve => setTimeout(resolve, ms));
}
}
Mocha test
it.only("test", () =>
{
let cut = new Foo();
let ASpy = sinon.spy(cut, 'A');
let BSpy = sinon.spy(cut, 'B');
sinon.stub(cut, 'sleep').resolves();
fakeClock.tick(1000);
ASpy.should.be.calledOnce;
BSpy.should.be.calledOnce;
ASpy.restore();
BSpy.restore();
});
The test above fails, because function B is called too late.
Result is:
A
1) test
B
When I modify the code, that it doesn't use the setIntervall it works.
Any hint how to get this test work?
Thanks in advance.
I found a "dirty" solution for my issue. Following test works as I want:
it.only("test", (done) =>
{
let cut = new Foo();
let ASpy = sinon.spy(cut, 'A');
let BSpy = sinon.spy(cut, 'B');
sinon.stub(cut, 'sleep').resolves();
fakeClock.tick(1000);
fakeClock.restore(); // restore timer so that following setTimeout works
setTimeout(() =>
{
ASpy.should.be.calledOnce;
BSpy.should.be.calledOnce;
ASpy.restore();
BSpy.restore();
done();
}, 0);
});
It's redundant but I am learning JS and I want to know how it really works.
Returning a function directly from a module
let func1 = function () {
let test = function () {
console.log("1");
}
return {
getTest : test
}
}
Returning a function by using a function
let func1 = function () {
let test = function () {
console.log("1");
}
return {
getTest : function () {
return test;
}
}
}
In the first case, the getTest property of your object points to a function, so calling it this way:
func1().getTest()
Should result in logging 1.
In the second case, getTest returns a function which returns another function, so you'd have to also call the result in order to get 1, this way:
func1().getTest()();
Calling just getTest will return your function object, rather than calling it.
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() {
// ...
}
}
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);
}
}
Considering code snippet below,
function one(){
var prm = new Promise(function(resolve,reject){
});
prm.customKey = function(){
}
return prm;
}
function two(){
return one().then(function(){
//something
});
}
Now calling the function two, returns a promise in which 'customKey' is missing
function three(){
return one();
}
But while doing the same thing in function three(without handling success using 'then'),
returns a promise which has 'customKey' in it.
Can someone clarify me whats really happening and why?
As you can see from "Mozilla Docs" then method returns new Promise (e.g: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then).
If you want to assign a function to retrieve / do something in resolve function do it like this:
var prm = new Promise((resolve, reject) => {
let customKeyFunction = () => {
console.log("I'm custom key function.");
};
resolve(customKeyFunction);
})
From now on you can call this function like that:
function two() {
return one().then(customKey => {
customKey();
})
}