Viable way to set LiveData off of the main thread - kotlin-coroutines

Using LiveData to signal a fragment to update it's display based on work done off of the main thread can be done by the postValue() function instead of setValue() but it has the problem that every posted value may not be observed on the main thread in the fragment.
A simple way to solve the problem is set the value from the worker running on Dispatchers.IO using withContext. Below is simple extension function that decides if this is needed using #OptIn(ExperimentalStdlibApi::class), which suggests this is a bad idea:
companion object {
#OptIn(ExperimentalStdlibApi::class)
suspend fun WorkingDepth.resetDepthOnMain() {
if (currentCoroutineContext()[CoroutineDispatcher].toString() == "Dispatchers.Main") {
resetDepth()
} else {
coroutineScope {
withContext(Dispatchers.Main) {
// This is the technique to ensure that the main thread sees
// the notification. If done off the main thread via a post
// it is not guaranteed that the observer will get the notification.
resetDepth()
}
}
}
}
}
The "opt in" annotation is avoided by treating all dispatchers as not Dispatchers.Main. Probably the unnecessary overhead is inconsequential, but the nagging question remains why is it not built in to "set on main" instead of "post and hope off of main".
Setting off of main throws an exception, so another way is the catch the exception and then switch threads and try again.
In a simple example where the live data is supposed to show landmarks along a work path it was seen that some expected landmarks do not get shown with "post and hope".

The withContext() method already includes the optimization suggested in the question.
This is explained in the comment for the fast paths in the coroutine library:
// FAST PATH #1 -- new context is the same as the old one
...
// FAST PATH #2 -- the new dispatcher is the same as the old one (something else changed)
...
Bottom line: If you must ensure execution on Main just wrap in a withContext(Dispatchers.Main) block.

Related

Solution for Dexie.js debugging code taking over on deferred promises

I'm using Dexie.js for handling indexed storage. However, it would take over exception handling on any process launched by the deferred promise. This made it very difficult to catch the bugs where they happened.
This is what works. I would love to get input from other developers if this is a good idea.
function isolate(deferred) {
return {
exec: function() {
var args = arguments;
var timeout = setTimeout(function() {
deferred.apply(this, args);
clearTimeout(timeout);
},1);
}
};
}
function save(name, drawing, data, onComplete) {
return db.drawings.put(
{ name: name, drawing, data: data
).then(isolate(onComplete).exec);
}
This way the promise function completes, Dexie.js is very happy about it, and then a new one starts in the timeout, which Dexie (or any other promise style handler) doesn't care about anymore.
If you need to debug your code and break when an error occurs, you can do that also in Promise-based code without having to do your suggested workaround.
In the F12 debugger in Chrome, check "Pause on caught exceptions". Also make sure to blackbox 3rd part libs so you don't end up breaking on feature testings in babel, jquery, dexie. This is simply done by right-clicking somewhere on the script code (when debugger breaks into it) and choose "blackbox". (Make sure NOT to blackbox your own code though!)
Now the debugger will break when an error occur in your code no matter if it's launched from a promise callback.

Async/Await - not awaiting the async method [duplicate]

This question already has answers here:
Am I right to ignore the compiler warning for lacking await for this async call?
(3 answers)
Closed 7 years ago.
Below is my code. Compiler gives warning because AddLog is not awaited. I do not want to await this call and want to continue executing next lines. I dont have any concern if the exception is consumed also. Is it fine to ignore the warning?
public async Task Add()
{
this.AddLog( "Add executing" );
// Logic to Add Customer
}
public async Task AddLog( string message )
{
// Write to DB
}
Assuming you truly want to call the AddLog method in a fire-and-forget way, then you have a few options.
If, by design, you want AddLog to always be invoked as a fire-and-forget method, then you could change the signature to not return a Task.
public async void AddLog( string message ) // change Task to void
{
// Write to DB
// WARNING: Make sure that exceptions are handled in here.
}
However, if you do this, you better make sure that exceptions are properly handled from within the AddLog method. If any exception goes unhandled, it will crash your process.
Another option is to change the way you invoke AddLog to clearly state your intent that you don't care about when the Task completes, or about any exceptions that may be raised. You can do this by defining an empty continuation (Well, almost empty. See my edit at the bottom of the post for why it's a good idea to read the Task.Exception property at the very least).
// see EDIT for why the Task.Exception property is read here.
this.AddLog("Add executing").ContinueWith(t => { var observed = t.Exception; });
With either option, unless you are awaiting on other code inside your Add method that you are not showing us, then there is no longer any point in defining your Add method as async. You can simply turn it into a regular synchronous method. Otherwise, you'll then get another warning telling you that This async method lacks 'await' operators and will run synchronously....
public void Add() // no need for "async Task"
{
// see EDIT for why the Task.Exception property is read here.
this.AddLog("Add executing").ContinueWith(t => { var observed = t.Exception; });
// Logic to Add Customer
}
In any case, I wouldn't simply ignore the warning. Much like sometimes we get the warning Use of unassigned local variable 'x' in cases where we know that our code is fine, we typically don't ignore the warning. Instead, we may explicitly initialize the variable to null just to make our intent clear, and make the warning go away. Similarly, you can make the warning go away by making your intentions more explicit to the compiler using one of the above options.
EDIT: Word of caution about unobserved exceptions
I should also mention that even with the ContinueWith option, you may have to be careful about unhandled exceptions that come from your AddLog method.
According to this article, the way unobserved exceptions from tasks are handled has changed between .NET 4.0 and .NET 4.5. So, if you are still running .NET 4.0, or if you forcing .NET 4.0 exception behavior via configuration, you run the risk that unhandled exceptions will crash your process whenever the task gets GC-collected and finalized.
To make sure that this is not a problem, you can adjust the continuation to explicitly observe the exception, if any is present. You don't actually need to do anything with it, you just need to read it. This is one way to do it safely:
this.AddLog("Add executing").ContinueWith(t => { var observed = t.Exception; });
I've updated my earlier examples above to use the safer version of the continuation.
I would make add() non async since it isn't...and then task.run on add log

Observing changes in HealthKit data using HKObserverQuery

When I setup an HKObserverQuery, the update handler always gets immediately called (something I didn't expect). It also gets called when I add data points through Health.app, as you would expect. I am tending to think I am not doing something right with the completion handler, but the docs are fairly sparse on what is supposed to happen here.
Question: Below is basically what I'm doing. Is this expected behavior, or am I missing something?
func listenForUpdates() {
let bodyMassType = HKQuantityType.quantityTypeForIdentifier(HKQuantityTypeIdentifierBodyMass)
let updateHandler: (HKObserverQuery!, HKObserverQueryCompletionHandler!, NSError!) -> Void = { query, completion, error in
if !error {
println("got an update")
// ... perform a sample query to get the actual data
completion() // is this the right thing to do?
} else {
println("observer query returned error: \(error)")
}
}
let query = HKObserverQuery(sampleType: bodyMassType, predicate: nil, updateHandler: updateHandler)
healthStore?.executeQuery(query)
}
Edit: discovered completion handler should only be called when there wasn't an error, so moved into the !error block. An error is present when the app is not authorized.
Yes, this is expected behavior. The update handler will always be called on first execution so that you can use it to fetch your initial data (from your sample query, anchored object query, etc) and populate your UI.
The completion handler is only necessary if you intend to use background delivery, it informs HealthKit that you have received and processed the data you need so that HealthKit knows to stop launching your app in the background. If you have not registered your app for background delivery, then the completion handler is essentially a no-op and you don't need to worry about it.

Prevent re-loading of javascript if functions already exist. Otherwise ensure synchronous loading

Using JQuery.load(), I change the content of my website's mainWindow to allow the user to switch between tabs. For each tab, there is one or multiple scipts that contain functions that are executed once the tab content is loaded.
Obviously when switching to the tab for the first time, the script has to be fetched from the server and interpreted, but this shouldn't happen if the user switches back to the tab later on. So, to put it short:
Load() html
make sure javascript functions exist, otherwise load script and interpret it.
call a a function on the javascript after the DOM is rebuilt.
Step one and two have to be complete before step 3 is performed.
At the moment, I am using nested callbacks to realize this:
function openFirstTab(){
$("#mainWindow").load("firstTab.php", function(){
if(typeof(onloadfFirstTab) != "function"){
jQuery.getScript("assets/js/FirstTab.js", function(){
onloadFirstTab();
});
}
else{
onloadFirstTab();
}
} );
}
but I feel that there should be a better way.
You can't write the code entirely synchronously since you can't load script synchronously after page load ( unless you do a synchronous XHR request and eval the results - not recommended ).
You've got a couple of choices. There are pre-existing dependency management libs like RequireJS which may fit the bill here, or if you just need to load a single file you can do something like this to clean up your code a bit rather than using if/else:
function loadDependencies() {
// For the sake of example, the script adds "superplugin" to the jQuery prototype
return $.getScript( "http://mysite.com/jquery.superplugin.js" );
}
function action() {
// If superplugin hasn't been loaded yet, then load it
$.when( jQuery.fn.superplugin || loadDependencies() ).done(function() {
// Your dependencies are loaded now
});
}
This makes use of jQuery Deferreds/Promises to make the code much nicer.
If you don't want to load the JS more than once and you are going to dynamically load it, then the only way to know whether it's already loaded is to test for some condition that indicates it has already been loaded. The choices I'm aware of are:
The simplest I know of is what you are already doing (check for the existence of a function that is defined in the javascript).
You could also use a property on each tab (using jQuery's .data() that you set to true after you load the script.
You could write the dynamically loaded code so that it knows how to avoid re-initializing itself if it has already been loaded. In that case, you just load it each time, but the successive times don't do anything. Hint, you can't have any statically defined globals and you have to test if it's already been loaded before it runs its own initialization code.
(Haven't tested it yet, so I am not sure if it works, especially since I didn't yet really understand scope in javascript:)
function require(scripts, callback){
var loadCount = 0;
function done(){
loadCount -=1;
if (loadCount==0){
callback();
}
}
for ( var script in scripts){
if (!script.exitsts()){
loadCount +=1;
jQuery.getScript(script.url, done);
}
}
}
This function takes an array of scripts that are required and makes sure all of them are interpreted before it calls the callback().
The "script" class:
function script(url, testFunc){
this.url =url;
this.testFunction = testFunc;
this.exists = function(){
if(typeof(testFunction)=="function"){
return true;
}
else{
return false;
}
}
}
Where the test-function is a function that is defined (only) in the concerned script.
PS:
To enable caching in JQuery and thus prevent the browser from doing a GET request every time getScript() is called, you can use one of the methods that are presented here.
Even though unnecessary GET - requests are avoided, the script is still getting interpreted every time getScript() is called. This might sometimes be the desired behavior. But in many cases, there is no need to re-interpret library functions. In these cases it makes sense to avoid calling getScript() if the required library functions are already available. (As it is done in this example with script.exists().

Adding custom code to mootools addEvent

Even though I've been using mootools for a while now, I haven't really gotten into playing with the natives yet. Currently I'm trying to extend events by adding a custom addEvent method beside the original. I did that using the following code(copied from mootools core)
Native.implement([Element, Window, Document], {
addMyEvent:function(){/* code here */}
}
Now the problem is that I can't seem to figure out, how to properly overwrite the existing fireEvent method in a way that I can still call the orignal method after executing my own logic.
I could probably get the desired results with some ugly hacks but I'd prefer learning the elegant way :)
Update: Tried a couple of ugly hacks. None of them worked. Either I don't understand closures or I'm tweaking the wrong place. I tried saving Element.fireEvent to a temporary variable(with and without using closures), which I would then call from the overwritten fireEvent function(overwritten using Native.implement - the same as above). The result is an endless loop with fireEvent calling itself over and over again.
Update 2:
I followed the execution using firebug and it lead me to Native.genericize, which seems to act as a kind of proxy for the methods of native classes. So instead of referencing the actual fireEvent method, I referenced the proxy and that caused the infinite loop. Google didn't find any useful documentation about this and I'm a little wary about poking around under the hood when I don't completely understand how it works, so any help is much appreciated.
Update 3 - Original problem solved:
As I replied to Dimitar's comment below, I managed to solve the original problem by myself. I was trying to make a method for adding events that destroy themselves after a certain amount of executions. Although the original problem is solved, my question about extending natives remain.
Here's the finished code:
Native.implement([Element, Window, Document], {
addVolatileEvent:function(type,fn,counter,internal){
if(!counter)
counter=1;
var volatileFn=function(){
fn.run(arguments);
counter-=1;
if(counter<1)
{
this.removeEvent(type,volatileFn);
}
}
this.addEvent(type,volatileFn,internal);
}
});
is the name right? That's the best I could come up with my limited vocabulary.
document.id("clicker").addEvents({
"boobies": function() {
console.info("nipple police");
this.store("boobies", (this.retrieve("boobies")) ? this.retrieve("boobies") + 1 : 1);
if (this.retrieve("boobies") == 5)
this.removeEvents("boobies");
},
"click": function() {
// original function can callback boobies "even"
this.fireEvent("boobies");
// do usual stuff.
}
});
adding a simple event handler that counts the number of iterations it has gone through and then self-destroys.
think of events as simple callbacks under a particular key, some of which are bound to particular events that get fired up.
using element storage is always advisable if possible - it allows you to share data on the same element between different scopes w/o complex punctures or global variables.
Natives should not be modded like so, just do:
Element.implement({
newMethod: function() {
// this being the element
return this;
}
});
document.id("clicker").newMethod();
unless, of course, you need to define something that applies to window or document as well.

Resources