I have a function that is executed each time a user updates an entry, in order to keep the list of entries up-to-date. The problem I'm running into is that when I run .clearCache() on the index, even though I'm doing it before the actual search function is run, it takes two reloads in order to get the latest updates. It's acting as if it's a reload behind. I have no idea what's causing this, but here is my search function:
search() {
this.results = [];
// Clear the algolia cache
this.auditionsIndex.clearCache();
this.auditionsIndex.search('', options).then(result => {
if(result.hits && result.hits.length > 0) {
this.results = result.hits;
}
// Clear the cache one more time
this.auditionsIndex.clearCache();
});
}
Changing something in an Algolia index happens asynchronously, when you execute something like index.saveObject() the result of the Promise/callback will be an object with taskId. You can then index.waitTask for that task, and send some event to your frontend to let it know to clear the cache and search.
see https://www.algolia.com/doc/api-reference/api-methods/wait-task/#methods
Related
I am working on a E2E test for a single-page web application in Angular2.
There are lots of clickable tags (not redirected to other pages but has some css effect when clicking) on the page, with some logic between them. What I am trying to do is,
randomly click a tag,
check to see the the response from the page is correct or not (need to grab many components from the web to do this),
then unclick it.
I set two const as totalRound and ITER, which I would load the webpage totalRound times, then within each loading page, I would randomly choose and click button ITER times.
My code structure is like:
let totalRound: number = 10;
let ITER: number = 100;
describe('XX Test', () => {
let page: AppPage;
beforeEach(() => {
page = new AppPage();
});
describe('Simulate User\'s Click & Unclick',() => {
for(let round = 0; round < totalRound; round++){
it('Click Simulation Round ' + round, () =>{
page.navigateTo('');
let allTagFinder = element.all(by.css('someCSS'));
allTagFinder.getText().then(function(tags){
let isMatched: boolean = True;
let innerTurn = 0;
for(let i = 0; i < ITER; i++){
/* Randomly select a button from allTagFinder,
using async func. eg. getText() to get more info
about the page, then check if the logic is correct or not.
If not correct, set isMatchTemp, a local variable to False*/
isMatched = isMatched && isMatchTemp;
innerTurn += 1;
if(innerTurn == ITER - 1){
expect(isMatched).toEqual(true);
}
}
});
});
}
});
});
I want to get a result after every ITER button checks from a loading page. Inside the for loop, the code is nested for async functions like getText(), etc..
In most time, the code performs correctly (looks the button checkings are in sequential). But still sometimes, it seems 2 iterations' information were conflicted. I guess there is some problem with my code structure for the async.
I thought JS is single-thread. (didn't take OS, correct me if wrong) So in the for loop, after all async. function finish initialization, all nested async. function (one for each loop) still has to run one by one, as what I wish? So in the most, the code still perform as what I hope?
I tried to add a lock in the for loop,
like:
while(i > innerTurn){
;
}
I wish this could force the loop to be run sequentially. So for the async. func from index 1 to ITER-1, it has to wait the first async. finish its work and increment the innerTurn by 1. But it just cannot even get the first async. (i=0) back...
Finally I used promise to solve the problem.
Basically, I put every small sync/async function into separate promises then use chaining to make sure the later function will only be called after the previous was resolved.
For the ITER for loop problem, I used a recursion plus promise approach:
var clickTest = function(prefix, numLeft, ITER, tagList, tagGsLen){
if(numLeft == 0){
return Promise.resolve();
}
return singleClickTest(prefix, numLeft, ITER, tagList, tagGsLen).then(function(){
clickTest(prefix, numLeft - 1, ITER, tagList, tagGsLen);
}).catch((hasError) => { expect(hasError).toEqual(false); });
}
So, each single clicking test will return a resolve signal when finished. Only then, the next round will be run, and the numLeft will decrease by 1. The whole test will end when numLeft gets to 0.
Also, I tried to use Python to rewrite the whole program. It seems the code can run in sequential easily. I didn't met the problems in Protractor and everything works for my first try. The application I need to test has a relatively simple logic so native Selenium seemed to be a better choice for me since it does not require to run with Frond-end code(just visit the webapp url and grab data and do process) and I am more confident with Python.
Given a list of functions, I wish the user to be able to select any of the functions to run at startup. How can this be done so that the user can "save" their choice of function to run the next time the code is run ie what would function runSelectedFunction (below) look like since you can't "save" a javascript function to file? Also, assume the list of potential functions is extensible.
const first = ()=>{
console.log('first');
}
const second = ()=>{
console.log('second');
}
const third = ()=>{
console.log('third');
}
loadUserSelectedFunctionFromDB()
.then(runSelectedFunction)
To be clear, the goal is to persist the user choice even if the code stops executing and is restarted. Normally, this would be done by storing a value in a database but the question is how to store a reference to a function in a database given an extensible set of functions?
Use a map like this:
const m = {
first, second, third
};
let selectFuncName = "first"; // from user selection, maybe click a button
let selectFunc = m[selectFuncName];
loadUserSelectedFunctionFromDB()
.then(runSelectedFunction)
In my Typescript/Ionic application, I have infinite scrolling that calls async operation like this:
When the view is first loaded, it calls getData with page = 1, and when the scroll reaches the end of the page it then calls getNextPage, which just increments the page count then directly calls getData. The getData function is as follows:
getData() {
this.provider.getData(this.page).subscribe(data => {
if(this.page == 1) {
this.items = data;
this.firstPageData = data;
}
else
for(let d of data)
this.items.push(d);
});
}
I use firstPageData to quickly reload the first page when the user performs an action and returns back to the main view where sees the data items again.
However, when I use firstPageData, it contains all items of the scrolled pages and not only the first page! I tried to debug the code and understand what is going on there but with no luck.
Am I missing something here? Please help.
I'm creating an android app that logs how long a person spends on certain things. I want to add the time spent to the total time spend, so I know how long a user has spent on an exercise type
I want to do it in a function, since I think it's easier than transactions.
exports.addExerciseTime = functions.database.ref('users/{userid}/stats/exerciseTime/{type}').onWrite( event =>{
console.log("Exercise time updates...");
var newdata = event.data.val();
var oldData = event.data.previous.val();
return event.data.ref.update(oldData+ newdata);
});
Now, I know that this function will loop until firebase shuts it down.
But how would I do this? Is there an easier way to do this?
you have an easy option of adding a flag indicating that you updated the data. next time you will get into the function, just start by checking if the flag exists in if so, exit the function. the con of this one is that you will run the function at least n+1
another option, according to their latest post, you know have a "onUpdate" and "onCreate" triggers as well. you might be able to use them smartly to optimize this even more (for example: only on first creation do XYZ, so it won't run on each update).
https://firebase.googleblog.com/2017/07/cloud-functions-realtime-database.html
Like you are saying, onWrite will capture every writing event. My solution would be replacing onWrite with onCreate, however let the user write to another path because Firebase will keep triggering the function. Besides that, your approach this is not the best solution since the updates can conflict. The use of transactions is better. That would look like this:
exports.addExerciseTime = functions.database.ref('users/{userid}/stats/exerciseTime/{type}').onCreate( event =>{
console.log("Exercise time updates...");
var newdata = event.data.val();
const pathToValue = //create the path here to exercisetime
return pathToValue.transaction(function(exercisetime) {
return (exercisetime || 0) + newdata;
});
});
*Notice the onCreate event instead of onWrite. Again: You will need to write it to a other path.
LAST EDIT: Try Tracker.afterFlush in the subscription-ready callback.
EDIT: The sorting was not the problem, it was the new subscription to the same collection with new session variable. Problem occurred because of Meteor not kicking old documents before triggering the subscribe-onReady callback...
I have a Meteor helper that returns a sorted collection (mapped documents).
It looks like this:
"currentNames": function () {
if (Session.get("sortBy") === "rating") {
return MyCollection.find({'name': {$exists: true}}, {sort: {rating: -1}}).map(function (document, index) {
document.index = index;
return document;
});
}
else if (Session.get("sortBy") === "alphabet") {
return MyCollection.find({'name': {$exists: true}}, {sort: {name: 1}}).map(function (document, index) {
document.index = index;
return document;
});
}
}
The sort works beautiful. I have a Template using this helper in a {{#each currentNames}}-loop which also works. But when I change the type of sort by changing the Session field sortBy my shown results (the html-dom-elems) are totally whirling around (changing their positions) until they found their final sort. I'm talking about 100 documents in the collection I'm sorting.
Because I don't want my users to see this sorting process live, I want to wait until the sort is finished. But I can not figure out a way to wait until the sort finished. I know when to hide the results (this is when the session variable sortBy is changed), but I dont have a callback or something when the sort is finished. But I need this.
Thank you for your time! Hope you can help me.
From meteor guide:
It’s also worth knowing a little about what happens on the server when the new subscription is started and the old one is stopped.
The server explicitly waits until all the data is sent down (the new subscription is ready) for the new subscription before removing the data from the old subscription. The idea here is to avoid flicker—you can, if desired, continue to show the old subscription’s data until the new data is ready, then instantly switch over to the new subscription’s complete data set.
What this means is in general, when changing subscriptions, there’ll be a period where you are over-subscribed and there is more data on the client than you strictly asked for. This is one very important reason why you should always fetch the same data that you have subscribed to (don’t “over-fetch”).
So in your case, you can hide your view (or show loading text etc.), until your new subscription is ready. You can check it with subscription handlers ready() function (which is reactive).