(Translation) asynchronous programming techniques

Description link

This tutorial describes the different ways asynchronous programming

For decades, as a developer, we are faced with a problem to be solved - how to prevent our application process is blocked. Whether we are in the development of desktop, mobile or server-side application process, we want to avoid allowing users to wait, or worse, leading to bottlenecks in the application process can not be extended.

There are many ways to solve this problem, including:

  • Thread
  • Callback
  • Futures, Promises 等等
  • Extended response
  • Coroutine

Thread

So far, the thread is probably the most common way to avoid blocking the application process.

1
2
3
4
5
6
7
8
9
10
fun (item: Item) {
val token = preparePost()
val post = submitPost(token, item)
processPost(post)
}

fun preparePost(): Token {

return token
}

Let us assume that in the above code, preparePost is a long-running process, thus blocking the user interface. We can do is to start it in a separate thread. This will allow us to avoid blocking UI. This is a very common technique, but there are a number of disadvantages:

  • Thread is not cheap. Require expensive thread context switching.
  • Thread is not unlimited. The number of threads that can be started by the restrictions of the underlying operating system. On the server side application process, which may lead to a serious bottleneck.
  • Thread is not always available. Some platforms, such as JavaScript does not even support thread
  • Thread is not easy. Debugging threads, to avoid race conditions is a common problem we face in multithreaded programming.

Callback

Callbacks, our idea is to pass a function as a parameter to another function, and this function is called after the process is complete.

1
2
3
4
5
6
7
8
9
10
11
12
fun postItem(item: Item) {
preparePostAsync { token ->
submitPostAsync(token, item) { post ->
processPost(post)
}
}
}

fun preparePostAsync(callback: (Token) -> Unit) {
// make request and return immediately
// arrange callback to be invoked later
}

In principle, this feels like a more elegant solution, but there are a few questions:

  • Nested difficulty callback. Typically, the callback function is usually used as final needs its own callback. This led to a series of nested callbacks, making it difficult to understand the code. This mode is often referred to as the title Christmas tree (tree branch on behalf of braces).
  • Error handling is very complicated. Nested error handling and dissemination of the model becomes more complex.

Callback is very common in the event loop architecture such as JavaScript class, but even there, usually people have turned to other methods, such as promises or responsive extension.

Futures,Promises 等等

Future Promise idea or behind (also referred to other terms according to the language / platform) is that when we called, we promise at some point it will return an object called Promise, and can operate on this object.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
fun postItem(item: Item) {
preparePostAsync()
.thenCompose { token ->
submitPostAsync(token, item)
}
.thenAccept { post ->
processPost(post)
}

}

fun preparePostAsync(): Promise<Token> {

return promise
}

This approach requires a series of changes to our programming, especially

  • Different programming models. Similarly with the callback, the imperative programming model transition from a top-down approach with a combined model chained calls. The traditional process structure (e.g., loop, exception handling, etc.) generally no longer effective in this model.
  • Different API. Usually need to learn a new API, such as thenCompose or thenAccept, they may vary by platform.
  • Specific return type. The return type is not the actual data we need, but returns a new type Promise must be introspective.
  • Error handling can be complex. Error propagation and links are not always straightforward.

Extended response

Reactive Extensions (Rx) by the introduction Erik Meijer C #. Although it does use on the .NET platform, but it does not reach mainstream adoption until Netflix to migrate to Java, named RxJava. Since then, it has provided a number of ports for a variety of platforms, including JavaScript (RxJS).

The idea behind Rx is turned to so-called observed flow, we will now be treated as data stream (unlimited data), and can observe these streams. In fact, Rx only with a series of extended observer mode, which allows us to operate on the data.

In the process it and Futures are very similar, but it can be treated as return Future discrete elements to return a stream Rx. However, unlike previous similar, it also introduces a new way of thinking about our programming model, it is famous

"Everything flows, it is observable"

This means that a different approach to the problem, and considerable changes have taken place from the way we use when writing synchronization code. One of the benefits of Futures contrary, given that it was ported to so many platforms, we can usually find a consistent API experience, whether we use it, whether it is C #, Java, JavaScript or Rx available in any other language.

In addition, Rx does introduce a better error handling.

Coroutine

kotlin use asynchronous code is to use collaborative process, which is to suspend the calculation of the idea that a pause function can be executed at a certain time and the idea of ​​recovery later.

One of the benefits of collaborative process, when it comes to developers to write code to write non-blocking blocking code is basically the same. Programming model itself has not really changed.

In the following code as an example

1
2
3
4
5
6
7
8
9
10
11
12
fun postItem(item: Item) {
launch {
val token = preparePost()
val post = submitPost(token, item)
processPost(post)
}
}

suspend fun preparePost(): Token {
// makes a request and suspends the coroutine
return suspendCoroutine { /* ... */ }
}

This code will start the long-running operations, and does not block the main thread. preparePost may suspend function is called, so keywords add a prefix to suspend it. As mentioned above, this means that the function will be executed at a certain time, suspend and resume.

  • Signature features remain the same. The only difference is that the pause is added to it. But the return type is the type we want to return.
  • Code is still being written as if we're writing synchronization code, from top to bottom, it does not require any special syntax, in addition to using a function called launch, which basically started the collaborative process (described in other tutorial).
  • Programming model and API remains unchanged. We can continue to use the loop, exception handling, etc., but do not need to learn a whole new API
  • It has nothing to do with the platform. Whether we are for the JVM, JavaScript or any other platform, the code we write is the same. Under performance compiler is responsible for its adaptation to each platform.
  • Collaborative process is not a new concept, not to mention the Kotlin invented. They have existed for decades, and Go is very popular in other programming languages. It is noteworthy that, in their implementation in Kotlin, most functions are delegated to the library. In fact, in addition to suspend a keyword, the language does not add additional keywords. This is slightly different from the language like C #, these languages ​​have asynchronous and wait for the part of the syntax. Use Kotlin, these are just library functions.

For more information about the process and the different possibilities of collaboration, please see the Reference Guide.

Original: Large column  (translated) asynchronous programming techniques


Guess you like

Origin www.cnblogs.com/petewell/p/11422406.html