I came across the following code:
let timeoutHandler;
clearTimeout(timeoutHandler);
timeoutHandler = setTimeout(() => {...});
This is an overly simplification since the original code is contained in a Vue application as follow:
public handleMultiSelectInput(value): void {
if (value === "") {
return;
}
clearTimeout(this.inputTimeoutHandler);
this.inputTimeoutHandler = setTimeout(() => {
axios.get(`${this.endpoint}?filter[${this.filterName}]=${value}`)
.then(response => {
console.log(response);
})
}, 400);
}
Does this mean this is some kind of cheap-ass debounce function?
Could someone explain what this exactly means.
Yes, it is a debounce function, which is when we wait for some amount of time to pass after the last event before we actually run some function.
There are actually many different scenarios where we might want to debounce some event inside of a web application.
The one you posted above seems to handle a text input. So it's debouncing the input, meaning that instead of fetching that endpoint as soon as the user starts to enter some character into the input, it's going to wait until the user stops entering anything in that input. It appears it's going to wait 400 milliseconds and then execute the network request.
The code you posted is kind of hard to read and understand, but yes, that is the idea of it.
I would have extracted out the network request like so:
const fetchData = async searchTerm => {
const response = await axios.get(`${this.endpoint}?filter[${this.filterName}]=${value}`);
console.log(response.data);
}
let timeoutHandler;
const onInput = event => {
if (timeoutHandler) {
clearTimeout(timeoutHandler);
}
timeoutHandler = setTimeout(() => {
fetchData(event.target.value);
}, 400);
}
Granted I am just using vanilla JS and the above is inside a Vuejs application and I am not familiar with the API the user is reaching out to. Also, even what I offer above could be made a lot clearer by hiding some of its logic.
Related
So i'm trying to lazy load articles with infinite scrolling, with inertia-vue and axios, backend is laravel 6.0. Since I don't want to do unnecessary request i'm giving over the total amount of articles to the component. I'm also tracking the amount of already loaded articles in the component data.
props: {
article_count: Number,
},
data: function () {
return {
loaded: 0,
scrolledToBottom: false,
articles: [],
}
},
The articles that are loaded are all being put into articles, and scrolledToBottom keeps track if the user has scrolled to the bottom. I have already checked if this refers to the vue component and has the data properties, which it does.
methods: {
load: function (nextToLoad) {
for (let i = nextToLoad; i < nextToLoad + 10; i++){
if (this.loaded <= this.article_count){
this.$axios.get(route('api_single_article', i))
.then(response => {
if (response.status == 200 && response.data != null) {
console.log(this.loaded);
this.articles.push(response.data);
}
this.loaded++;
})
.catch(error => {
if (error.response.status != 404) {
console.log(error);
console.log(this.loaded);
this.loaded++;
}
});
}
}
console.log(this.loaded);
},
scroll: function () {
window.onscroll = () => {
let bottomOfWindow = document.documentElement.scrollTop + window.innerHeight === document.documentElement.offsetHeight;
if (bottomOfWindow) {
this.load(this.loaded + 1);
}
}
}
}
The weird part is, that if i log this.loaded in the response / error arrow function it always returns 0. If i do it outside the response it also returns 0, but if i log this the loaded property has the supposed value after the loop has ran through, despite it being logged every time the loop is run. I have already showed this to a friend and he also couldn't fix it. The behaviour just seems weird to me and i don't know how else i should do, since i'm very inexperienced with vue.
Thanks for the help.
Might be a concurrency thing. Remember that the axios call is asynchronous so the loop will continue before the responses have been received.
So it could be the many responses are received at the same time and that loaded is then still 0 at that point. Looks like this won't be the case. Apparently JavaScript will process callbacks one after the other.
I'm not sure how JavaScript handles concurrent increments though so I can't exactly explain how that plays into effect here.
From your implementation, it looks like you'd want to turn the load function into an asynchronous function (using the async) keyword and then use the await before the axios call to ensure that you have received a response before making another request.
For interest's sake, maybe have a look at your network tab and count how many requests are being made. I suspect there will be more requests made than this.article_count.
The cypress docs(https://docs.cypress.io/guides/core-concepts/variables-and-aliases.html#Elements) are pretty unclear on how alias and variables can be used to store information during a test.
I'm trying to store the text of a div on one page to use later, for example:
// let vpcName;
it('Store current name to use later', () => {
// save name for later use - doesn't work
// cy.get('#value').then(elem => {
// vpcName = Cypress.$(elem).text;
// });
// using alias - also doesn't work
cy.get('#value')
.invoke('text')
.as('vpcName');
});
it('Use previous value to return to correct page', () => {
cy.contains(this.vpcName).click();
});
I just came across this article that explains how and why you store a variable and then use it later in the "Cypress way":
https://www.stevenhicks.me/blog/2020/02/working-with-variables-in-cypress-tests/
In respect to how it should work, here is my example which first, collects the message (the message is shown for only 3 secs then disappears). Secondly, it gets the value using the # sign. Lastly my code passes the stored message through to an empty function constructed to assert that the value Portsmouth is contained within the message.
it('Current Port change', () => {
cy.get('#viewport').find('div[id=message]').then(message => {
let wags = message;
cy.wrap(wags).as('wags')
});
cy.get('#wags').then(wags => {
expect(wags).to.contain("Portsmouth")
});
});
let me know if you need further clarification
Try this:
cy.get('button').then(($btn) => {
const txt = $btn.text()
// $btn is the object that the previous command yielded
})
Source: https://docs.cypress.io/guides/core-concepts/variables-and-aliases.html#Return-Values
I had to resort to
writeFile
readFile
When I was trying to save cookies from one test to another in Cypress
An alternative solution which doesn't actually store the element to a variable but serves some of the same purpose is to wrap the cy.get call in a function. Such as:
const btn = () => cy.get('[data-testid="reset-password-button"]')
btn().should('have.attr', 'disabled')
cy.get('[data-testid="new-password-input"]').type('someNewPW')
btn().should('not.have.attr', 'disabled')
This could be a good option if readability is your main goal.
I've been struggling with this for few days. Here is my approach if you want to use the saved text (for example) multiple times. However it seems too long to me and I think it could be optimized. Tested on Cypress v11.2.0
cy.xpath("//tbody/tr[2]/td[2]").then(($text) => {
let txt = $text.text()
//expect(txt).to.eq('SomeText')
cy.wrap(txt).as('txt')
})
cy.get('#txt').then(txt => {
//expect(txt).to.contain("SomeText")
cy.xpath("xpath of search field").type(txt)
})
I'm trying to get .then() to work with .on() but it only works with .once().
I get playersRef.on(...).then is not a function, when trying it with on()
So what I have now is this:
let nodesRef = Firebase.database().ref('players')
let answers = {}
playersRef.on('value', (playersSnapshot) => {
playersRef.once('value').then((playersSnapshot) => {
playersSnapshot.forEach((playerSnapshot) => {
let playerKey = playerSnapshot.key
let answersRef = playerSnapshot.child('answers')
if (answersRef .exists()){
answers[playerKey ] = answersRef .val()
}
})
}).then(() => {
console.info(answers);
dispatch(setPlayerAnswers(answers))
})
})
But I don't like the fact that it is querying the ref twice.
How would I go about getting each player's answer in this example? What would be a more correct way? Because I don't think this is the way the Firebase devs intended this.
And what is the reasoning that it is not implemented for .on()? Because playersSnapshot.forEach(...).then is also not a function... How do I execute something only once whenever the .on('value') triggers?
firebaser here
I'm not entire certain of parts of your question, so will only answer the parts that I can.
what is the reasoning that [then()] is not implemented for .on()?
We implemented support for Promises in the Firebase JavaScript clients. The definition of a promise is that it resolves (or fails) only once. Since the Firebase on() methods can receive updated data multiple times, it does not fit in the definition of a promise.
How do I execute something only once whenever the .on('value') triggers?
When you want to respond only once to an event, you should use the once() method.
I'm not sure why you're having problems with forEach(). The following should work:
playersRef.on('value', (playersSnapshot) => {
playersSnapshot.forEach((playerSnapshot) => {
let playerKey = playerSnapshot.key
let answersRef = playerSnapshot.child('answers')
if (answersRef .exists()){
answers[playerKey ] = answersRef .val()
}
})
})
If you're having trouble making this work, can you reproduce that problem in a jsbin?
Earlier I ran into the issue of Alexa not changing the state back to the blank state, and found out that there is a bug in doing that. To avoid this issue altogether, I decided that I wanted to force my skill to always begin with START_MODE.
I used this as my reference, where they set the state of the skill by doing alexa.state = constants.states.START before alexa.execute() at Line 55. However, when I do the same in my code, it does not work.
Below is what my skill currently looks like:
exports.newSessionHandler = {
LaunchRequest () {
this.hander.state = states.START;
// Do something
}
};
exports.stateHandler = Alexa.CreateStateHandler(states.START, {
LaunchRequest () {
this.emit("LaunchRequest");
},
IntentA () {
// Do something
},
Unhandled () {
// Do something
}
});
I'm using Bespoken-tools to test this skill with Mocha, and when I directly feed IntentA like so:
alexa.intended("IntentA", {}, function (err, p) { /*...*/ })
The test complains, Error: No 'Unhandled' function defined for event: Unhandled. From what I gather, this can only mean that the skill, at launch, is in the blank state (because I have not defined any Unhandled for that state), which must mean that alexa.state isn't really a thing. But then that makes me wonder how they made it work in the example code above.
I guess a workaround to this would be to create an alias for every intent that I expect to have in the START_MODE, by doing:
IntentA () {
this.handler.state = states.START;
this.emitWithState("IntentA");
}
But I want to know if there is a way to force my skill to start in a specific state because that looks like a much, much better solution in my eyes.
The problem is that when you get a LaunchRequest, there is no state, as you discovered. If you look at the official Alexa examples, you will see that they solve this by doing what you said, making an 'alias' intent for all of their intents and just using them to change the state and then call themselves using 'emitWithState'.
This is likely the best way to handle it, as it gives you the most control over what state and intent is called.
Another option, assuming you want EVERY new session to start with the same state, is to leverage the 'NewSession' event. this event is triggered before a launch request, and all new sessions are funneled through it. your code will look somewhat like this:
NewSession () {
if(this.event.request.type === Events.LAUNCH_REQUEST) {
this.emit('LaunchRequest');
} else if (this.event.request.type === "IntentRequest") {
this.handler.state = states.START;
this.emitWithState(this.event.request.intent.name);
}
};
A full example of this can be seen here (check out the Handlers.js file): https://github.com/alexa/skill-sample-node-device-address-api/tree/master/src
I would also recommend reading through this section on the Alexa GitHub: https://github.com/alexa/alexa-skills-kit-sdk-for-nodejs#making-skill-state-management-simpler
EDIT:
I took a second look at the reference you provided, and it looks like they are setting the state outside of an alexa handler. So, assuming you wanted to mimic what they are doing, you would not set the state in your Intent handler, but rather the Lambda handler itself (where you create the alexa object).
exports.handler = function (event, context, callback) {
var alexa = Alexa.handler(event, context);
alexa.appId = appId;
alexa.registerHandlers(
handlers,
stateHandlers,
);
alexa.state = START_MODE;
alexa.execute();
};
I'm creating an interactive webpage with RxJs.
This is what I want to achieve:
I have an application that generates tokens. These tokens can be consumed by an external entity.
When a user creates a token, the page starts polling the webserver for its status (consumed or not). When the token is consumed, the page refreshes.
So, when the token is created, a request is sent to the server every 2 seconds asking whether the token is consumed yet.
I have an Observable of strings that represent my generatedTokens.
I actually already have a working implementation using the Rx.Scheduler.default class, which allows me to do things manually. However, I can't help but feel that there should be a much simpler, more elegant solution to this.
This is the current code:
class TokenStore {
constructor(tokenService, scheduler) {
// actual implementation omitted for clarity
this.generatedTokens = Rx.Observable.just(["token1", "token2"]);
this.consumedTokens = this.generatedTokens
.flatMap(token =>
Rx.Observable.create(function(observer) {
var notify = function() {
observer.onNext(token);
observer.onCompleted();
};
var poll = function() {
scheduler.scheduleWithRelative(2000, function() {
// tokenService.isTokenConsumed returns a promise that resolves with a boolean
tokenService.isTokenConsumed(token)
.then(isConsumed => isConsumed ? notify() : poll());
}
);
};
poll();
}));
}
}
Is there something like a "repeatUntil" method? I'm looking for an implementation that does the same thing as the code above, but looks more like this:
class TokenStore {
constructor(tokenService, scheduler) {
// actual implementation omitted for clarity
this.generatedTokens = Rx.Observable.just(["token1", "token2"]);
this.consumedTokens = this.generatedTokens
.flatMap(token =>
Rx.Observable.fromPromise(tokenService.isTokenConsumed(token))
.delay(2000, scheduler)
// is this possible?
.repeatUntil(isConsumed => isConsumed === true));
}
}
Funnily enough the answer struck me a few minutes after posting the question. I suppose rubberducking might not be so silly after all.
Anyway, the answer consisted of two parts:
repeatUntil can be achieved with a combination of repeat(), filter() and first()
fromPromise has some internal lazy cache mechanism which causes subsequent subscriptions to NOT fire a new AJAX request. Therefore I had to resort back to using Rx.Observable.create
The solution:
class TokenStore {
constructor(tokenService, scheduler) {
// actual implementation omitted for clarity
this.generatedTokens = Rx.Observable.just(["token1", "token2"]);
this.consumedTokens = this.generatedTokens
.flatMap(token =>
// must use defer otherwise it doesnt retrigger call upon subscription
Rx.Observable
.defer(() => tokenService.isTokenConsumed(token))
.delay(2000, scheduler)
.repeat()
.filter(isConsumed => isConsumed === true)
.first())
.share();
}
}
A minor sidenote: the "share()" ensures that both observables are hot, which avoids the scenario where every subscriber would cause ajax request to start firing.
class TokenSource {
constructor(tokenService, scheduler) {
this.generatedTokens = Rx.Observable.just(["token1", "token2"]).share();
this.consumedTokens = this.generatedTokens
.flatMap(token =>
Rx.Observable.interval(2000, scheduler)
.flatMap(Rx.Observable.defer(() =>
tokenService.isTokenConsumed(token)))
.first(isConsumed => isConsumed === true))
.share()
}
}
You can take advantage of two facts:
flatMap has an overload that takes an observable which will be resubscribed to every time a new event comes in
defer can take a method returning a promise. The method will be re-executed every subscription, which means you do not have to roll your own Promise->Observable conversion.