Giter Club home page Giter Club logo

asyncawait's Introduction

Async/Await

A Kotlin library for Android to write asynchronous code in a simpler and more reliable way using async/await approach, like:

async {
   progressBar.visibility = View.VISIBLE
   // Release main thread and wait until text is loaded in background thread
   val loadedText = await { loadFromServer() }
   // Loaded successfully, come back in UI thread and show the result
   txtResult.text = loadedText
   progressBar.visibility = View.INVISIBLE
}

As you see in the example above, you can write asynchronous code in a imperative style, step by step. Calling await to run code in background doesn't lock the UI thread. And execution continues in UI thread after background work is finished. There is no magic, see how it works.

Dependency

compile 'co.metalab.asyncawait:asyncawait:1.0.0'

Usage

async

Coroutine code has to be passed as a lambda in async function

async {
   // Coroutine body
}

await

Long running code has to be passed as a lambda in await function

async {
   val result = await {
      //Long running code
   }
   // Use result
}

You may have many await calls inside async block, or have await in a loop

async {
   val repos = await { github.getRepos() }
   showList(repos)
   repos.forEach { repo ->
      val stats = await { github.getStats(repo.name) }
      showStats(repo, stats)
   }
}

awaitWithProgress

Use it to show loading progress, its second parameter is a progress handler.

val loadedText = awaitWithProgress(::loadTextWithProgress) {
         // Called in UI thread
         progressBar.progress = it
         progressBar.max = 100
      }

A data loading function (like the loadTextWithProgress above) should have a functional parameter of type (P) -> Unit which can be called in order to push progress value. For example, it could be like:

private fun loadTextWithProgress(handleProgress: (Int) -> Unit): String {
   for (i in 1..10) {
      handleProgress(i * 100 / 10) // in %
      Thread.sleep(300)
   }
   return "Loaded Text"
}

Handle exceptions using try/catch

async {
   try {
      val loadedText = await {
         // throw exception in background thread
      }
      // Process loaded text
   } catch (e: Exception) {
      // Handle exception in UI thread
   }
}

Handle exceptions in onError block

Could be more convenient, as resulting code has fewer indents. onError called only if exception hasn't been handled in try/catch.

async {
   val loadedText = await {
      // throw exception in background thread
   }
   // Process loaded text
}.onError {
   // Handle exception in UI thread
}

Unhandled exceptions and exception delivered in onError wrapped by AsyncException with convenient stack trace to the place where await been called originally in UI thread

finally execution

finally always executed after calling onError or when the coroutine finished successfully.

async {
   // Show progress
   await { }
}.onError {
   // Handle exception
}.finally {
   // Hide progress
}

Safe execution

The library has Activity.async and Fragment.async extension functions to produce more safe code. So when using async inside Activity/Fragment, coroutine won't be resumed if Activity is in finishing state or Fragment is detached.

Avoid memory leaks

Long running background code referencing any view/context may produce memory leaks. To avoid such memory leaks, call async.cancelAll() when all running coroutines referencing current object should be interrupted, like

override fun onDestroy() {
      super.onDestroy()
      async.cancelAll()
}

The async is an extension property for Any type. So calling [this.]async.cancelAll intrerrupts only coroutines started by [this.]async {} function.

Common extensions

The library has a convenient API to work with Retrofit and rxJava.

Retorift

  • awaitSuccessful(retrofit2.Call)

Returns Response<V>.body() if successful, or throws RetrofitHttpError with error response otherwise.

async {
   reposList = awaitSuccessful(github.listRepos(userName))
}

rxJava

  • await(Observable)

Waits until observable emits first value.

async {
   val observable = Observable.just("O")
   result = await(observable)
}

How to create custom extensions

You can create your own await implementations. Here is example of rxJava extension to give you idea. Just return the result of calling AsyncController.await with your own lambda implementation. The code inside await block will be run on a background thread.

suspend fun <V> AsyncController.await(observable: Observable<V>): V = this.await {
   observable.toBlocking().first()
}

How it works

The library is built upon coroutines introduced in Kotlin 1.1.

The Kotlin compiler responsibility is to convert coroutine (everything inside async block) into a state machine, where every await call is a non-blocking suspension point. The library is responsible for thread handling, error handling and managing state machine. When background computation is done the library delivers result back into UI thread and resumes coroutine execution.

asyncawait's People

Contributors

louiscad avatar pilgr avatar ryanleecode avatar tsuharesu avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

asyncawait's Issues

Add support for Support Fragments

Fragments from the support-v4 library are recommended for usage over native Fragments, given they are always up-to-date on bug fixes and when new features are added. Plus, it is more likely that someone will be using Support Fragments in their app rather than native Fragments.

Question: Code execution sequence

Thanks for this library - it is really nice to see Async/Await coming to Android. I have a question about the sequence of execution. Consider this example

async {
   progressBar.visibility = View.VISIBLE
   // Release main thread and wait until text is loaded in background thread
   val loadedText = await { loadFromServer() }
   // Loaded successfully, come back in UI thread and show the result
   txtResult.text = loadedText
   progressBar.visibility = View.INVISIBLE
}
someOtherCodeHere()
yetMoreAwesomeCodeHere()

I understand that when we hit await { loadFromServer() }, the execution of the async block is suspended and the control proceeds to someOtherCodeHere(). Is this right?

Also, what happens later when loadFromServer() returns? I assume then txtResult.text = loadedText and the rest of the async block is executed. But, then what? Where does the execution proceed after an async block that had been suspended is un-suspended?

Callback for cancelling

Given a situation where UI elements depend on the AsyncController (like progress bars) while having to cancel tasks, it's necessary to know when a task was cancelled in order to hide progress and such

Proguard rules for generate signed app

Warning:co.metalab.asyncawait.RetrofitExtensionsKt: can't find referenced class retrofit2.Call
Warning:co.metalab.asyncawait.RetrofitExtensionsKt$awaitSuccessful$1: can't find referenced class retrofit2.Call
Warning:co.metalab.asyncawait.RetrofitExtensionsKt$awaitSuccessful$1: can't find referenced class retrofit2.Response
Warning:co.metalab.asyncawait.RetrofitExtensionsKt$awaitSuccessful$1: can't find referenced class retrofit2.Call
Warning:co.metalab.asyncawait.RetrofitExtensionsKt$awaitSuccessful$1: can't find referenced class retrofit2.Response
Warning:co.metalab.asyncawait.RetrofitExtensionsKt$awaitSuccessful$1: can't find referenced class retrofit2.Call
Warning:co.metalab.asyncawait.RetrofitHttpError: can't find referenced class retrofit2.Response
Warning:co.metalab.asyncawait.RxExtensionsKt: can't find referenced class rx.Observable
Warning:co.metalab.asyncawait.RxExtensionsKt$await$1: can't find referenced class rx.Observable
Warning:co.metalab.asyncawait.RxExtensionsKt$await$1: can't find referenced class rx.observables.BlockingObservable
Warning:co.metalab.asyncawait.RxExtensionsKt$await$1: can't find referenced class rx.Observable
Warning:there were 29 unresolved references to classes or interfaces.
Warning:Exception while processing task java.io.IOException: Please correct the above warnings first.
Error:Execution failed for task ':app:transformClassesAndResourcesWithProguardForRelease'.

Job failed, see logs for details

Support for idling resource

Hi,

I was just wondering how hard is to implement/expose some sort of mechanism to be able to create idling resources that wait for the coroutines to finish.

I can try sending you a PR if you point me in the right direction on what you think is the best way to do it. I think it'd be a great addition to the library.

Parallel execution?

When I try to execute several tasks in parallel my code looks ugly.

async {
           await {
               async {
                   await {
                       async {
                           await {
                               val it = System.currentTimeMillis()
                               while (System.currentTimeMillis() - it < 5000);
                               Log.w("Executing coroutine", "Task1")
                           }
                       }
                       val it = System.currentTimeMillis()
                       while (System.currentTimeMillis() - it < 5000);
                       Log.w("Executing coroutine", "Task2")
                   }
               }
               val it = System.currentTimeMillis()
               while(System.currentTimeMillis() - it < 5000 );
               Log.w("Executing coroutine", "Task3")
           }

           Log.w("Executing coroutine", "All tasks completed")
       }

Is any way to make parallel execution looks more elegant?
Something like this

 async {
            awaitParallel  {
                val it = System.currentTimeMillis()
                while (System.currentTimeMillis() - it < 5000);
                Log.w("Executing coroutine", "Task1")
            }
            awaitParallel  {
                val it = System.currentTimeMillis()
                while (System.currentTimeMillis() - it < 5000);
                Log.w("Executing coroutine", "Task2")
            }
            awaitParallel  {
                val it = System.currentTimeMillis()
                while (System.currentTimeMillis() - it < 5000);
                Log.w("Executing coroutine", "Task3")
            }
            Log.w("Executing coroutine", "All tasks completed")
        }

Don't include app in maven library

There is a conflict when you include an AndroidManifest.xml in your library release.

Error:Execution failed for task ':app:processDebugManifest'.
> Manifest merger failed : Attribute application@allowBackup value=(false) from AndroidManifest.xml:20:9-36
  	is also present at [co.metalab.asyncawait:asyncawait:1.0.0] AndroidManifest.xml:12:9-35 value=(true).
  	Suggestion: add 'tools:replace="android:allowBackup"' to <application> element at AndroidManifest.xml:18:5-280:19 to override.

How does this compare to kotlinx-coroutines-android?

Hi,
I discovered JetBrains (and probably other contributors) made a coroutines Android implementation on the kotlinx.couroutines repo, and I assume you were aware of this while building this library.

Could you add a word in the README.md or another file in the repo on how your implementation and the kotlinx one compare together in different regards such as performance, API design and features (and more if useful)?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.