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);
});
Related
I am trying to call the function test() as many times as possible in a given time interval.
Here the function should be running for 15 seconds.
function test(): void; // Only type def
function run() {
return new Promise(resolve => {
setTimeout(() => {
resolve();
}, 15000); // 15 seconds
while (true) {
test();
}
});
}
run()
.then(() => {
console.log('Ended');
});
However, the function doesn't stop running, and the Ended console.log does not appear. (Promise not resolved obviously). Is there a way to achieve this in Javascript ?
I was wondering, I could probably use console timers and put the condition in the while statement ? (But is that the best way ?)
The reason why your function does not stop executing is because resolving a promise does not stop script executing. What you want is to store a flag somewhere in your run() method, so that you can flip the flag once the promise is intended to be resolved.
See proof-of-concept below: I've shortened the period to 1.5s and added a dummy test() method just for illustration purpose:
let i = 0;
function test() {
console.log(`test: ${i++}`);
}
function run() {
return new Promise(resolve => {
let shouldInvoke = true;
setTimeout(() => {
shouldInvoke = false;
resolve();
}, 1500); // 15 seconds
const timer = setInterval(() => {
if (shouldInvoke)
test();
else
window.clearInterval(timer);
}, 0);
});
}
run()
.then(() => {
console.log('Ended');
});
I have two questions from the following example:
Why is does x log before y?
Why is x a Promise?
I'm expecting bar to wait for foo to resolve with the value 'Hello' before it logs and then returns it.
let foo = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('Hello');
}, 2000);
})
}
let bar = async () => {
let y = await foo();
console.log(y);
return y;
}
let x = bar();
console.log(x);
What I'm expecting to see is
'Hello' // console.log(y)
'Hello' // console.log(x)
What I'm getting is
Promise {<pending>} // console.log(x)
'Hello' // console.log(y)
Shouldn't bar wait for foo to resolve before returning y?
Any help would be much appreciated!
Here is the documentation about async function: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function
And Promise object: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
It is stated that an async function returns a Promise object which is in a pending state until it is resolved.
Now let's look at your code:
You assign to x the return value of bar(), which is a Promise, since there is no await keyword to stop the execution (And you can not add one, because you are outside of an async function) this promise is logged as pending because the async function bar() has not yet returned.
Meanwhile, in the async function bar() you assign to y the return value of the async function foo() which is also a Promise, but this time you make it await for the result. After 2 seconds of waiting time, the promise is fulfiled and y is logged with the parameter of the resolve() method, which is Hello.
Thus the behaviour you witness is the expected one.
You need to await the function:
let example = async () => {
let foo = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('Hello');
}, 2000);
})
}
let bar = async () => {
let y = await foo();
console.log(y);
return y;
}
let x = await bar();
console.log(x);
}
example();
You should use an await before calling bar() but, actually, you can use await just when you wrapped it by a async function like below:
async function Func() {
let foo = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('Hello');
}, 2000);
})
}
let bar = async () => {
let y = await foo();
console.log(y);
return y;
}
let x = await bar();
console.log(x);
}
When you execute the Func(); your expectation will fire.
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);
}
}
Is there any way I can make the following work:
function A
This function returns a promise that does a task which uses multiple webworkers, once the webworkers have collectively finished this task it will resolve the promise.
function B
This function executes function A, and after that it executes multiple other functions that require A to have been done first.
function C
This function executes function B, and after that it executes multiple other functions that require A. This function may onle be called once, as it is a single process which improves a certain genetic algorithm task by executing function B multiple times sequentially. (so no callback B -> C).
Something like this:
var someObject = {test: null};
function A () {
return new Promise((resolve, reject) => {
// do some task which requires webworkers
someObject.test = 10;
});
}
async function B(){
await A();
someObject.test *= 4;
return someObject.test;
}
function C(){
// set up complicated options here
// a loop using these options
var i = 100;
while(i--){
var x = B();
console.log(x);
// do tasks that require B;
}
}
C();
View it here on JSFiddle, please note that I want to avoid any kind of callbacks because I am already working with a complicated object architecture (i'm doing neuro-evolution).
The best thing I could think of is somehow wrapping function B in a promise as well, but I haven't figured out how to do that.
You can as well call await on async functions as they implicitly return a promise:
var someObject = {test: null};
function A () {
return new Promise((resolve, reject) => {
// do some task which requires webworkers
someObject.test = 10;
setTimeout(resolve, 1000);
});
}
async function B(){
await A();
someObject.test *= 4;
return someObject.test;
}
async function C(){
// set up complicated options here
// a loop using these options
var i = 100;
while(i--){
var x = await B();
console.log(x);
// do tasks that require B;
}
}
C();
You could return a promise from B(). Note, you don't need to resolve someObject in B() (i.e. resolve(someObject.test)) because it's a global var, but I did in order to demonstrate how to resolve a value and to follow your example code. Also note I removed the loop for example sake.
EDIT - thanks to #t.niese and others for pointing out the anti-pattern in my original answer. Please see the updated answer, below
https://jsfiddle.net/ey07zy6h/9/
var someObject = {test: null};
function A () {
console.log('in a')
return new Promise((resolve, reject) => {
// do some task which requires webworkers
someObject.test = 10;
resolve()
});
}
function B() {
console.log('in b')
return A()
.then(() => new Promise((resolve, reject) => {
someObject.test *= 4;
resolve(someObject.test);
}))
.catch(e => {
console.log('Err in A: ' + e)
})
}
function C() {
// set up complicated options here
console.log('in c')
// a loop using these options
var i = 100;
// while(i--) {
B()
.then(data => {
let x = data;
console.log('X is: ' + JSON.stringify(x));
// do tasks that require B;
})
.catch(e => {
console.log('Err in B', e)
})
// }
}
C();
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.