Changes in cache are not saved without async/await - javascript

I have a react-native application, it uses react-navigation. There is a functional component with a handler for button click.
I recently had a problem with async/await. I called async method in a non-async method and it did not work as I expected. I debugged it a little and I found out that the async method is called and does everything it should but after that the changes are lost.
The non-async method looked like this:
const handleDone = () => {
api.events.removeEventFromCache(eventId);
navigation.navigate(routes.Events);
};
When the method is called, an object is removed from cache and user is navigated to another screen. api.events.removeEventFromCache(eventId) got called and finished successfully and I even check the cache to see that the object was removed. The thing is that after the navigation.navigate(routes.Events) it is suddenly still in the cache.
Adding async/await keyword solved the problem but I do not really understand why:
const handleDone = async () => {
await api.events.removeEventFromCache(eventId);
navigation.navigate(routes.Events);
};
I though it would do everything without waiting for the result but why did the result disappear? It is not a question about the order of executing and waiting for the result. I do not really care about the result, I just want it to be done.
This is the log made without the keywords:
--> in cache now 3
remove the event from cache
navigate to events
cache length before remove 3
--> in cache now 3
cache length set 2
cache length checked 2
--> in cache now 3
A log with the keywords:
--> in cache now 3
remove the event from cache
cache length before remove 3
cache length set 2
cache length checked 2
navigate to events
--> in cache now 2
Yes, there is a difference in execution but my question is about the result in cache.

When you log the output before and after navigation, you are logging in 2 different contexts.
To explain this, lets say you have a cache object cache from which you wish to remove the event.
The way your code without the keywords executes is as follows:
cache is loaded by the api method to be edited
navigation method executes and it is going to send a copy of the current cache to the next screen and discard the previous.
cache-copy is created and dispatched by the navigation method.
You api method is currently still working with the cache object and not cache-copy.
cache is edited by the api method but is then discarded as the new screen is now using the cache-copy object.
In the second scenario:
The api method receives cache
The event is removed from cache
The navigation method receives the updated cache and creates cache-copy
cache-copy now has the updates list of events
The important thing to note is where and when exactly the cache-copy object is being created. If it is created before the event is removed, the code will work just fine.
Lets say, your navigation method executes the exact instant when the api method has removes the event, your code will run as expected even if async/await isn't used.

async/await is just working as expected. When managing promises, you could have two options:
//Using promises
const handleDone = () => {
api.events.removeEventFromCache(eventId).then(() => {
navigation.navigate(routes.Events);
});//You can manage failure with .catch()
};
and using async/await just as you posted, it waits until the promise is executed, it doesn't stop everything itself. Also, it is a good practice to wrap it inside a try/catch block in case the Promise fails.
The Promise object represents the eventual completion (or failure) of an asynchronous operation, and its resulting value.
That means, when you call api.events.removeEventFromCache(eventId) it won't be completed immediately, so you either have to use one of both options.

Related

Using asynchronous output in render loop

I want to render the output of an async process into the three.js renderer/scene. I came up with 2 ways, both have 2 'loops' though the second one seems more parallel than the other:
Works, you can try it here: The render is ran, which triggers the async process but immediately renders what it has (empty scene), and schedules itself to be run again requestAnimationFrame(render), which doesn't happen immediately. When the async process completes, the results are added to the three.js scene, but is not rendered immediately. The render loop will render it when its ready, which repeats.
Motivation for not wanting to do this: I initially had a possible memory leak issue probably because I was not disposing the resources (geometry, material), and to dispose resources in this style seemed complicated, I came up with the next option:
A loop which does async processing on the video feed and saves it to a variable (in my case, i use a face mesh library (written at the bottom of the question). A threeJS render loop then takes the saved variable (output of async processing), and puts it in a Buffer and renders it. The first method added it to the three.js immediately, whereas this waits.
The issue with my code here is that the 2 loops clump together: loads of render loop run in a row, then loads of async processes run in a row. I discovered this in the debugger/ breakpoints.
Example of Async process:
NPM: #mediapipe/face-mesh API
const faceMeshModel = new FaceMesh({locateFile: (file) => {
return `https://cdn.jsdelivr.net/npm/#mediapipe/face_mesh/${file}`;
}});
// Write the results handler
this.model.onResults((results) => {
/* Preprocess the results into a shape that works with three.js */
// I've got 2 options here: either call the render loop code directly (works) or
// Save this result into a class field/ variable, and access this in the render loop.
// Run the async process here once previous results are generated, so its a loop
faceMeshModel.send({image: htmlVideoElement})
})
// Actually run the process:
faceMeshModel.send({image: htmlVideoElement})

ProtractorJS Timeout on clicking element, any suggestions?

The Problem
I have a simple 'New Booking' button I wish to click. As shown below
<button _ngcontent-c9="" class="mat-menu-item" mat-menu-item="" role="menuitem" routerlinkactive="menu-highlight-item-left" tabindex="0" ng-reflect-router-link="/booking,create" ng-reflect-router-link-active="menu-highlight-item-left" aria-disabled="false">
New Booking
<div class="mat-menu-ripple mat-ripple" matripple="" ng-reflect-disabled="false" ng-reflect-trigger="[object HTMLButtonElement]">
</div>
</button>
Something to Note
This is a Material 2 Angular 5 Application, so every page uses and functions off of Angular.
In the past, the code I will provide further down had worked 100% every single time I ran the test without fail, which leads me to believe something else in the source code may be what causing this issue. However no additional load/spinners or popups are in the way
It has absolutely no unique attributes whatsoever, no surrounding
divs with IDs to attach to or any other way of grabbing the element.
However using my knowledge of XPath, despite the problem I managed
to find a unique locator which I believe works, as shown below.
var btnNewBooking = element(by.xpath("//*[text()='New Booking']"));
I know for a fact this should grab the element, because when I query the xpath through the ChromeDevTools it highlights the element.
The Code that originally worked
var btnNewBooking = element(by.xpath("//*[text()='New Booking']"));
btnNewBooking.click();
The Exception produced from the ProtractorJS Console
Failed: Timed out waiting for asynchronous Angular tasks to finish after 11 seconds. This may be because the current page is not an Angular application. Please see the FAQ for more details: https://github.com/angular/protractor/blob/master/docs/timeouts.md#waiting-for-angular
While waiting for element with locator - Locator: By(xpath, //*[text()='New Booking'])
What i've tried so far?
I've tried using Protractors built in Expected Conditions from the API Library they have provided this includes all of these:
1) Waiting Until the Presence of the element
var EC = protractor.ExpectedConditions;
// Waits for the element with id 'abc' to be present on the dom.
browser.wait(EC.presenceOf($('#abc')), 5000);
2) Waiting Until the Visibility of the element
EC = protractor.ExpectedConditions;
// Waits for the element with id 'abc' to be visible on the dom.
browser.wait(EC.visibilityOf($('#abc')), 5000);
3) Waiting until the clickability of the element
var EC = protractor.ExpectedConditions;
// Waits for the element with id 'abc' to be clickable.
browser.wait(EC.elementToBeClickable($('#abc')), 5000);
I've also tried using Angular's own waits such as:
browser.WaitForAngular();
and also
browser.WaitForAngularEnabled();
The click() method returns a promise, if you need some documentation on that you can surely read the following: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
The Protractor/WebDriverJS "Control Flow" takes care of settling these promises. However, my team has been struggling with the synchronization not being perfect.
A solution to this issue is the actually use EC to await for the element and then settle the promise of the click by chaining
.then()
Check out this stack overflow answer: Protractor, when should I use then() after a click()
Even better?
We use the new async functions to await promises in our protractor tests. This has enabled us to refactor much of the logic and ensure a proper controlled flow. This link describes well in the Fork in the road section why a "serial" code (executes one step at a time) is the outcome of using async/await: https://ponyfoo.com/articles/understanding-javascript-async-await
You should try disabling Protractor to wait for being synchronous with Angular. This can be done by browser.ignoreSynchronisation. Set it to truein your conf.js file.

Meteorjs: HTTP.get is retrieving data but isn't returning anything to the helper function variable

I have the following code that reads a json file:
Meteor.methods({
getPlaces: function(){
return HTTP.get(Meteor.absoluteUrl("/places.json"), function(e, r) {
console.log(r.data);
return r.data;
});
}
})
The console shows that its retrieving the data just fine.
Here is the part of my helper function for the template I want to display 'Places' in:
testing: function(){
return Meteor.call("getPlaces");
}
& here is my loop in the template where it is supposed to show:
{{#each testing}}
<li>{{testing.name}}</li>
{{/each}}
But it seems like I'm not calling the function right as the loop doesn't show anything. I've tested the loop by giving it random data which it works fine with but whenever I call Meteor.call or even HTTP.get directly on 'testing' it doesn't give me anything.
There are two essential problems here:
You're using HTTP.get asynchronously when you don't need to.
You're trying to use Meteor.call synchronously when you can't as it won't work like that.
Explanation of 1:
In supplying a callback to HTTP.get, you're telling Meteor to allow code execution to continue beyond that line, and pass the result of the call to the callback function. As a result, the actual method function finishes execution and will return undefined (which will be passed back to the calling function as null via EJSON) well before your actual HTTP call has returned. When that happens, the result will be logged, but even though you're returning the results in the callback, the enclosing method function won't care as it will have completed execution long before.
There are several ways to deal with this, the simplest being: don't pass a callback. On the server, you can use HTTP.get synchronously by not passing a callback, in which case code will cease executing until the results come back, and they will actually be returned to the client. Note that you cannot do this if you use HTTP.get on the client. Other ways of dealing with this involve Futures or Promises (better), but are unnecessary here.
Explanation of 2:
This is more complicated to resolve, but is fundamental to Meteor and Javascript. If you're calling an asynchronous function and you want to use the result, you need to supply a callback (or use promises). You can't just expect it to work inline for the same reasons given above. So some changes need to be made:
Don't call a method from within a template helper. You've no idea how often the template helper will run (it depends on all sorts of reactive things), so this is an essentially unbounded amount of traffic on the websocket that you're committing to. Call them when something happens (template is rendered, event handler, a specific piece of data changes (i.e. within an autorun block)), but not in a helper function.
Store the result in a reactive data source, otherwise even if you successfully receive it and put it somewhere, your UI won't update with the results.
So:
Template.yourTemplate.onCreated(function () {
this.places = new ReactiveVar()
Meteor.call("getPlaces", (err, res) => {
// do some error handling here
this.places.set(res)
})
})
{{#each Template.instance.places.get}}
<li>{{name}}</li>
{{/each}}
A few notes:
You need to install the reactive-var package, which inexplicably isn't provided out of the box, for this to work: meteor add reactive-var.
You could return the data via a template helper which uses Template.instance().places.get(), but you can just do it in-line in the template, which seems easier to me.
If the result of the first method call aren't sufficient and you need to update the results, do this in an event handler, or an autorun block as required. If the server needs to be able to push data directly to the client rather than waiting for requests for an update, then methods are the wrong tool - you need to be using Meteor's pub/sub model.

looping through key/value pair of promises in protractor

I am using protractor to test a series of webpages (actually mostly one webpage of Angular design, but others as well). I have created a series of page objects to handle this. To minimize code maintenance I have created an object with key value pairs like so:
var object = {
pageLogo: element(by.id('logo')),
searchBar: element.all(by.className('searchThing')),
...
};
The assumption being that I will only need to add something to the object to make it usable everywhere in the page object file. Of course, the file has functions (assuming you are not familiar with the page object pattern) as such:
var pageNamePageObject = function () {
var object = {...}; //list of elements
this.get = function() {
brower.get('#/someWebTag');
}
this.getElementText = function(someName){
if (typeof someName == 'number')
... (convert or handle exception, whatever)
return object[name].getText();
}
...
*Note these are just examples and these promises can be handled in a variety of ways in the page object or main tests
The issue comes from attempting to "cycle" through the object. Given that the particular test is attempting to verify, among other things, that all the elements are on the particular web page I am attempting to loop through these objects using the "isPresent()" function. I have made many attempts, and for brevities sake I will not list them here, however they include creating a wrapper promise (using "Q", which I must admit I have no idea how it works) and attempting to run the function in the 'expect' hoping that the jasmine core will wait for all the looping promises resolve and then read the output (it was more of a last ditch effort really).
You should loop like you did before on all of the elements, if you want it in a particular order, create a recursive function that just calls itself with the next element in the JSON.
Now, to handle jasmine specs finishing before and that stuff.
this function needs to be added to protractor's flow control for it to wait to continue, read more about it here. and also, dont use Q in protractor, use protractor's implementation of webdriverJS promises.
Also, consider using isDisplayed instead, assuming you want it to be dispalayed on the page.
So basically, your code skeleton will look like this:
it(.....{
var flow = webdriver.promise.controlFlow();
return webdriver.execute(function () {//your checks on the page here,
//if you need extract to external function as i described in my first paragraph
well i think that should provide you enough info on how to handle waiting for promises in protractor, hope i helped.

dojo.data.objectStore.deleteItem

I have a dojo.store.Memory wrapped in a dojo.data.ObjectStore which I am then plugging into a dataGrid. I want to delete an item from the store and have the grid update. I have tried every combonation I can think of with no success. For example:
var combinedStore = new dojo.data.ObjectStore({objectStore: new dojo.store.Memory({data: combinedItems})});
combinedStore.fetch({query:{id: 'itemId'}, onComplete: function (items) {
var item = items[0];
combinedStore.deleteItem(item);
combinedGrid.setStore(combinedStore);
}});
combinedGrid.setStructure(gridLayout);
This throws no errors but combinedStore.objectStore.data still has the item that was meant to be deleted and the grid still displays the item. (The also seems to be a complete mismatch between combinedStore.objectStore.data and combinedStore.objectStore.index);
There's a simple solution, luckily! The delete is successfully happening, however, you need to save the ObjectStore after the deletion for it to be committed.
Change your code to look like this:
onComplete: function (items) {
var item = items[0];
combinedStore.deleteItem(item);
combinedStore.save();
combinedGrid.setStore(combinedStore);
}
That little save should do the trick. (Please note: the save must occur after the deleteItem - if you put it outside the fetch block, do to being asynchronous, it will actually happen before the onComplete!)
Working example: http://pastehtml.com/view/b34z5j2bc.html (Check your console for results.)
This does seem rather poorly documented at present in the new dojo.store documentation.
The old dojo.data.api.Write documentation make it fairly clear. An excerpt from http://dojotoolkit.org/reference-guide/dojo/data/api/Write.html:
Datastores that implement the Write interface act as a two-phase
intermediary between the client and the ultimate provider or service
that handles the data. This allows for the batching of operations,
such as creating a set of new items and then saving them all back to
the persistent store with one function call.
The save API is defined as asynchronous. This is because most
datastores will be talking to a server and not all I/O methods for
server communication can perform synchronous operations.
Datastores track all newItem, deleteItem, and setAttribute calls on
items so that the store can both save the items to the persistent
store in one chunk and have the ability to revert out all the current
changes and return to a pristine (unmodified) data set.
Revert should only revert the store items on the client side back to
the point the last save was called.
dojo.store has evolved from dojo.data and seems to follow many of its behavioral aspects.
The new dojo.store documentation http://www.sitepen.com/blog/2011/02/15/dojo-object-stores/ and http://www.sitepen.com/blog/2011/02/15/dojo-object-stores/ manages to talk specifically about the delete operation without mentioning having to call save() (in fact I can't find the word 'save' on that page at all).
I'm staying away from dojo.store as long as possible, hopefully it will be easier to follow in 1.7 or later, whenever I'm forced to use it for real :)

Categories

Resources