Issue with a recursive function in Cypress. Tests slowing down over time - recursion

I am working on a diagnostic application which can ask the user quite a large number of questions (100+). I had been using a recursive function to deal with the questions and their answering but i noticed over time the tests would slow down to a crawl and after a lot of research I've come to realize that its probably the recursion that's doing it. Any help would be much appreciated.
The current solution is this:
Cypress.Commands.add('intro_Questions', (data) => {
const intro_Questions_Loop = () => {
cy.get('#question_tree').then(question_tree => {//gets the current question tree
cy.get('#link').then(link => { //get the question number
if (action == 'LAST') {
return;
}
cy.Question({q: link, qt: question_tree, question_log: data.fixture}) //checks the question on screen vs question tree
cy.Answer({q: link, qt: question_tree}) //picks a random answer and wraps action with the next question code
cy.editAnswer({}) //edits questions at random dependant on set percentage
intro_Questions_Loop();
})
})
}
intro_Questions_Loop();
})
Any help to improve, make it more efficient or just to write it in a different way would be much appreciated!

Recursive functions can be memory-hogs, but 100 iterations isn't a large number.
Without seeing all the hidden details you haven't posted, I'd say the (main) problem is with the way you recurse into the synchronous function intro_Questions_Loop.
Try putting the recursive call on the Cypress command queue
...
cy.editAnswer({})
.then(() => {
intro_Questions_Loop()
})
which should give the test runner time to process each iteration before getting the next.
Other than that, it's hard to say because too much of your actual code is hidden inside custom commands.

Related

Single threaded asynchronous event loop with `winit`

I'm trying to build an NES emulator using winit, which entails building a game loop which should run exactly 60 times per second.
At first, I used std::thread to create a separate thread where the game loop would run and wait 16 milliseconds before running again. This worked quite well, until I tried to compile the program again targeting WebAssembly. I then found out that both winit::window::Window and winit::event_loop::EventLoopProxy are not Send when targeting Wasm, and that std::thread::spawn panics in Wasm.
After some struggle, I decided to try to do the same thing using task::spawn_local from one of the main asynchronous runtimes. Ultimately, I went with async_std.
I'm not used to asynchronous programming, so I'm not even sure if what I'm trying to do could work.
My idea is to do something like this:
use winit::{window::WindowBuilder, event_loop::EventLoop};
use std::time::Duration;
fn main() {
let event_loop = EventLoop::new();
let _window = WindowBuilder::new()
.build(&event_loop);
async_std::task::spawn_local(async {
// game loop goes here
loop {
// [update game state]
// [update frame buffer]
// [send render event with EventLoopProxy]
async_std::task::sleep(Duration::from_millis(16)).await;
// ^ note: I'll be using a different sleep function with Wasm
}
});
event_loop.run(move |event, _, control_flow| {
control_flow.set_wait();
match event {
// ...
_ => ()
}
});
}
The problem with this approach is that the game loop will never run. If I'm not mistaken, some asynchronous code in the main thread would need to be blocked (by calling .await) for the runtime to poll other Futures, such as the one spawned by the spawn_local function. I can't do this easily, since event_loop.run is not asynchronous.
Having time to await other events shouldn't be a problem, since the control flow is set to wait.
Testing this on native code, nothing inside the game loop ever runs. Testing this on Wasm code (with wasm_timer::Delay as the sleep function), the game loop does run, but at a very low framerate and with long intervals of halting.
Having explained my situation, I would like to ask: is it possible to do what I'm trying to do, and if it is, how would I approach it? I will also accept answers telling me how I could try to do this differently, such as by using web workers.
Thanks in advance!

Wait for resolving Observable for synchronous usage

I like to resolve my Observable res$ before continue with log-ready. But I found no way, to achieve that. I can't move my last log-line into async context. Is there a way to solve that problem?
let o1 = Observable.create((o) => {
console.log('o1 start');
setTimeout(() => {
console.log(`o1 is running`);
o.next('o1');
}, 1500);
console.log('o1 ends');
});
let o2 = Observable.create((o) => {
console.log('o2 starts');
setTimeout(() => {
console.log(`o2 is running`);
o.next('o2');
}, 1100);
console.log('o2 ends');
});
let res$ = o1.pipe(concatMap(() => o2)).subscribe();
//ToDO resolve res$ before continue execution
console.log(`ready`);
https://stackblitz.com/edit/rxjs-b9z3wn?devtoolsheight=60
Is there a way to solve that problem?
In short: No, this is impossible.
JavaScript is a single-theaded language. Asynchronous code is all run on the same thread such that two parts of your JavaScript code will never run at the same time.
This has some advantages. For example, you rarely need to worry about mutex's, semaphores, etc. With JavaScript you can be certain that all synchronous code will run to completion before any other code is run.
There is a downside, however, it's not possible to lift an asynchronous context into a synchronous one. Any attempt to do so will never halt (the way a program fails when you write an infinite loop). This is why Node.js was (still is for older code) infamous for callback hell. It relies heavily on shoving callbacks onto the event loop since it is never allowed to wait. Promises and Observables are two ways to manage this complexity.
JavaScript is single threaded. So if you wait for 10 seconds, the whole program will hang for 10 seconds and do nothing. If you try to wait for something else to happen, the single thread will be busy waiting for forever and therefore be too busy to go do the thing it's waiting for.
You can convert an Observable to a Promise and await it afterwards. Try this:
await o1.pipe(concatMap(() => o2)).toPromise()

Displaying data from Firebase on load of Ionic app

I'm a beginner in Ionic and Firebase. To learn using ionic+firebase, I'm writing a RandomQuote app to fetch a random entry from Firebase. A reload() method is called when I click a reload button, and the random quote is displayed as expected.
However, I also want the quote to display when the app is loaded, i.e., before I click the reload button. I call the reload() method in the constructor but it doesn't work. I have tried to search for answers on the web but cannot find anything that I could understand. Not sure if I'm searching the wrong keywords or in the wrong domains.
The following is the reload() method that I put in my FirebaseProvider class and called from my home.ts:
reload(){
this.afd.list('/quoteList/').valueChanges().subscribe(
data => {
this.oneQuote = data[Math.floor(Math.random() * data.length)];
}
)
return this.oneQuote;
}
Can anyone give me some hints? Or any pointer to useful books / materials for beginners will also be highly appreciated. Thank you very much.
Data is loaded from Firebase asynchronously. This means that by the time your return statement runs this.oneQuote doesn't have a value yet.
This is easiest to say by placing a few log statements around your code:
console.log("Before subscribing");
this.afd.list('/quoteList/').valueChanges().subscribe(
data => {
console.log("Got data");
}
)
console.log("After subscribing");
When you run this code, the output is:
Before subscribing
After subscribing
Got data
This is probably not what you expected. But it completely explains why your return statement doesn't return the data: that data hasn't been loaded yet.
So you need to make sure your code that needs the data runs after the data has been loaded. There are two common ways to do this:
By moving the code into the callback
By returning a promise/subscription/observable
Moving the code into the callback is easiest: when the console.log("Got data") statement runs in the code above, the data is guaranteed to be available. So if you move the code that requires the data into that place, it can use the data without problems.
Returning a promise/subscription/observable is a slightly trickier to understand, but nicer way to doing the same. Now instead of moving the code-that-needs-data into the callback, you'll return "something" out of the callback that exposes the data when it is available. In the case of AngularFire the easiest way to do that is to return the actual observable itself:
return this.afd.list('/quoteList/').valueChanges();
Now the code that needs the quotes can just subscribe to the return value and update the UI:
reload().subscribe(data => {
this.oneQuote = data[Math.floor(Math.random() * data.length)];
}
A final note: having a reload() method sounds like an antipattern. The subscription will already be called whenever the data in the quoteList changes. There is no need to call reload() for that.

Get an array from SpookyJs to Meteor

After a lot of hard work, my SpookyJS script works as I should and I got my spoils of war, an array of values I want to use to query my Collection in my Meteor app, but I have a huge problem.
I can't find a way to call any Meteor specific methods from spooky...
So my code is like this for the spooky.on function:
spooky.on('fun', function (courses) {
console.log(courses);
// Meteor.call('edxResult', courses); // doesn't work...
});
The console.log gives me the result I want:
[ 'course-v1:MITx+6.00.2x_3+1T2015',
'HarvardX/CS50x3/2015',
'course-v1:LinuxFoundationX+LFS101x.2+1T2015',
'MITx/6.00.1x_5/1T2015' ]
What I need is a way to call a Meteor.method with courses as my argument or a way to get access to the array in the current Meteor.method, after spookyjs finished it's work (Sadly I have no idea how to check whether spooky is finished)
My last idea would be to give the Meteor.method a callback function and store the array in the session or something, but that sounds like extremly bad design, there has to be a better way, I hope.
I am extremly proud of my little ghost, so any help to get it the last few pieces over the finish line would be extremly appricated.

How to debug slow meteor methods?

A number of my meteor methods have mysteriously slowed down recently. Whereas they used to be quite snappy, many are taking 10 or so seconds.
Things not causing the slowdown:
Additional functionality, the slowed sections of the codebase haven't been changed significantly
Machine load (cpu load hovering around 30%)
Additional DB load (no new queries added)
transfer time of the return data (returns undefined)
blocking method (I've tried with this.unblock() within the method)
I did debugging by using console.time() / console.timeEnd() on both the server and client side. The server side code takes about .3 seconds to run, but the client doesnt get the callback until about 11 seconds after the meteor.call() ...
This is the server method:
function cancelSomething(somethingId, reason) {
console.time('cancelSomething method');
check(somethingId, String);
check(reason, String);
if (!AuthChecks()))
throw new Meteor.Error(401, 'Not Authorized');
var something = MySomethings.findOne({'_id': somethingId});
if (!something)
throw new Meteor.Error(404, 'Something not found');
var returnVal = SomethingService.cancel(something, reason);
console.timeEnd('cancelSomething method'); // <--- prints "cancelSomething 350ms" or there abouts
return returnVal;
}
clientSide:
console.time('meteorCall');
Meteor.call('cancelSomething', this._id, reason, function(err) {
if (err) {
console.log('an error occurred', err);
}
console.timeEnd('meteorCall'); // <--- prints "meteorCall 11500" or so
});
EDIT:
I have noticed there is some correlation with the quantity of docs within the "somethings" in the db. w/ 500 documents it takes about 1 second to receive the return on the client, with 5000 it takes about 8.5 seconds ...
Interesting. I think this one is hard to answer without knowing more about your app, but I have some suggestions that could help steer you in the right direction.
blocking
If we can assume that the timers are working properly, what could be happening is that the server is unable to begin execution of the method because another method call by the same client is already in progress. Have a look at unblock in the docs. The answer may be as simple as putting a this.unblock at the top of one of your methods.
syncing
If SomethingService.cancel does something which results in a lot of DDP traffic, that could tie up the client for a while and make it unable to execute your callback. For example if it:
modified or created a lot of documents which then had to be synced to the client
indirectly caused an expensive subscription to be rerun (my money is on this one)
Updated Answer
It seems the problem had to to with a call to observe, which makes a lot of sense given your high CPU load. Frankly, it's a little silly that I didn't suggest this because I have already answered a similar question here. If you want to try to keep the observe, here are some additional suggestions:
Use oplog tailing.
Try to narrow the scope of the observe as much as possible using selectors which are currently supported by the oplog observe driver. The supported selectors should improve at or before meteor 1.0.
Limit the fields to only those you need.
Check that oplog tailing is working by adding the facts package.

Resources