All Projects → vjames19 → Kotlin Futures

vjames19 / Kotlin Futures

Licence: mit
A collections of extension functions to make the JVM Future, CompletableFuture, ListenableFuture API more functional and Kotlin like.

Programming Languages

kotlin
9241 projects

Projects that are alternatives of or similar to Kotlin Futures

Pyrogram
Telegram MTProto API Client Library and Framework in Pure Python for Users and Bots
Stars: ✭ 2,252 (+2750.63%)
Mutual labels:  async, library
Gitter Api
[production-ready] Gitter API implementation for php 7.0+ allowing sync, async and streaming access.
Stars: ✭ 11 (-86.08%)
Mutual labels:  async, library
Asynquence
Asynchronous flow control (promises, generators, observables, CSP, etc)
Stars: ✭ 1,737 (+2098.73%)
Mutual labels:  async, library
Igropyr
a async http server base on libuv for Chez Scheme
Stars: ✭ 85 (+7.59%)
Mutual labels:  async, library
Caf
Cancelable Async Flows (CAF)
Stars: ✭ 1,027 (+1200%)
Mutual labels:  async, library
Fasy
FP iterators that are both eager and asynchronous
Stars: ✭ 488 (+517.72%)
Mutual labels:  async, library
Parallel Ssh
Asynchronous parallel SSH client library.
Stars: ✭ 864 (+993.67%)
Mutual labels:  async, library
Restless
Express.js api, type safe validations and more
Stars: ✭ 32 (-59.49%)
Mutual labels:  async, library
Google Books Android Viewer
Android library to bridge between RecyclerView and sources like web page or database. Includes demonstrator (Google Books viewer)
Stars: ✭ 37 (-53.16%)
Mutual labels:  async, library
Handle Path Oz
Android Library to handle multiple Uri's(paths) received through Intents.
Stars: ✭ 36 (-54.43%)
Mutual labels:  async, library
Tanya
GC-free, high-performance D library: Containers, networking, metaprogramming, memory management, utilities
Stars: ✭ 70 (-11.39%)
Mutual labels:  async, library
Aiodine
🧪 Async-first Python dependency injection library
Stars: ✭ 51 (-35.44%)
Mutual labels:  async, library
C Ares
A C library for asynchronous DNS requests
Stars: ✭ 1,193 (+1410.13%)
Mutual labels:  async, library
Suave
Suave is a simple web development F# library providing a lightweight web server and a set of combinators to manipulate route flow and task composition.
Stars: ✭ 1,196 (+1413.92%)
Mutual labels:  async
Cat
Plain C library for parsing AT commands for use in host devices.
Stars: ✭ 77 (-2.53%)
Mutual labels:  library
Libqsbr
QSBR and EBR library
Stars: ✭ 76 (-3.8%)
Mutual labels:  library
Mailchimp Api Php
PHP library for v3 of the MailChimp API.
Stars: ✭ 75 (-5.06%)
Mutual labels:  library
Graphql client
GraphQL Client.
Stars: ✭ 78 (-1.27%)
Mutual labels:  library
Neuralpy
NeuralPy: A Keras like deep learning library works on top of PyTorch
Stars: ✭ 77 (-2.53%)
Mutual labels:  library
Php Library Starter Kit
A tool to quickly set up the base files of a PHP library project.
Stars: ✭ 75 (-5.06%)
Mutual labels:  library

kotlin-futures

A collections of extension functions to make the CompletableFuture API more functional and Kotlin like.

[

Table of Contents

Motivation

Having worked in Scala for some time and specifically using their Future API and then going back to Kotlin a language I ❤️ ❤️, I came to realize that the CompletableFuture API seems odd for defining and composing async operations.

Every time I use the CompletableFuture API I find myself going back to the documentation to double check what a given function would do.

Now this might be a matter of taste, but being heavily inspired by Scala's Future API, I decided to make this library to hopefully make the CompletableFuture API more functional and kotlin like

To achieve this I didn't want to introduce a new Future type and have to change any project to use the new Future type, hence by using extension functions and inlining we can have better API without any extra cost.

Download

Gradle

repositories {
    ...
    maven { url 'https://jitpack.io' }
}

dependencies {

    // for completable future
    compile 'com.github.vjames19.kotlin-futures:kotlin-futures-jdk8:<version>'

    // for listenable future
    compile 'com.github.vjames19.kotlin-futures:kotlin-futures-guava:<version>'
}

Maven

<repositories>
 <repository>
     <id>jitpack.io</id>
     <url>https://jitpack.io</url>
 </repository>
</repositories>

 <dependency>
    <groupId>com.github.vjames19</groupId>
    <artifactId>kotlin-futures</artifactId>
    <version>version</version>
</dependency>

For the rest: https://jitpack.io/#vjames19/kotlin-futures/

How to use

Every single operation accepts an Executor as its argument, by default it uses the ForkJoinPool.

For IO / blocking operations you should specify your own.

Creation

Creating a Future that runs on a given Executor (by default its the ForkJoinExecutor)

import io.github.vjames19.futures.jdk8.ForkJoinExecutor
import io.github.vjames19.futures.jdk8.Future
import java.util.concurrent.CompletableFuture
import java.util.concurrent.Executors

val future: CompletableFuture<Int> = Future { 10 }

// ForkJoinExecutor its just an alias ForkJoinPool.commonPool()
val futureOnForkJoin = Future(ForkJoinExecutor) { 10 }

val myExecutor = Executors.newSingleThreadExecutor()
val futureWithCustomExecutor = Future(myExecutor) {
    10
}

vs

val future: CompletableFuture<Int> = CompletableFuture.supplyAsync { 10 }

// ForkJoinExecutor its just an alias ForkJoinPool.commonPool()
val futureOnForkJoin = CompletableFuture.supplyAsync(Supplier { 10 }, ForkJoinExecutor)

val myExecutor = Executors.newSingleThreadExecutor()
val futureWithCustomExecutor = CompletableFuture.supplyAsync(Supplier { 10 }, myExecutor)

Creating immediate futures that run on the given thread.

import io.github.vjames19.futures.jdk8.ImmediateFuture
import io.github.vjames19.futures.jdk8.toCompletableFuture
import java.util.concurrent.CompletableFuture

val future: CompletableFuture<String> = ImmediateFuture { "Hello" }

val anotherImmediateFuture = "Hello".toString()

val aFailedImmediateFuture = IllegalArgumentException().toCompletableFuture<String>()

val futureWithTypeInference: CompletableFuture<String> = IllegalArgumentException().toCompletableFuture()

vs

val future: CompletableFuture<String> = CompletableFuture.completedFuture("Hello")

val aFailedImmediateFuture = CompletableFuture<String>().apply { completeExceptionally(IllegalArgumentException()) }

Composition

map

Map allows you to transform the success of this future into another future.

val future: CompletableFuture<String> = Future { 10 }.map { "Hello user with id: $it" }

vs

val future: CompletableFuture<String> = Future { 10 }
        .thenApplyAsync(Function { userId -> "Hello user with id: $userId" }, ForkJoinExecutor)

flatMap

flatMap allows you to do sequential composition. Creating a new future dependent on another one.

import io.github.vjames19.futures.jdk8.*
import java.util.concurrent.CompletableFuture


data class User(val id: Long, val name: String)
data class Post(val id: Long, val content: String)
data class UserPosts(val user: User, val posts: List<Post>)
fun fetchUser(id: Long): CompletableFuture<User> = Future { User(1, "Victor")}
fun fetchPosts(user: User): CompletableFuture<List<Post>> = Future { emptyList<Post>() }


// Fetching the posts depends on fetching the User
val posts = fetchUser(1).flatMap { fetchPosts(it) }

// Fetching both the user and the posts and then combining them into one
val userPosts =  fetchUser(1).flatMap { user ->
    fetchPosts(user).map { UserPosts(user, it) }
}

vs

val posts: CompletableFuture<List<Post>> = fetchUser(1)
        .thenComposeAsync(Function { fetchPosts(it) }, ForkJoinExecutor)

val userPosts: CompletableFuture<UserPosts> =  fetchUser(1)
        .thenComposeAsync(Function { user ->
            fetchPosts(user).thenApplyAsync(Function { posts ->
                UserPosts(user, posts)
            }, ForkJoinExecutor)
        }, ForkJoinExecutor)

flatten

flatten removes a level from a nested future.

import io.github.vjames19.futures.jdk8.*
import java.util.concurrent.CompletableFuture


val nestedFuture = Future { Future { 10 } }
val flattened: CompletableFuture<Int> = nestedFuture.flatten()

filter

filter will convert this future to a failed future if it doesn't match the predicate.

import io.github.vjames19.futures.jdk8.*

val future = Future { 10 }

// This future will succeed
val success = future.filter { it % 2 == 0 }

// This future will throw NoSuchElementException
val failed = future.filter { it % 3 == 0 }

zip

zip allows you to combine two futures and apply a transformation to it

import io.github.vjames19.futures.jdk8.*

val idFuture = Future { 10 }
val nameFuture = Future { "Victor" }

val helloFuture = nameFuture.zip(idFuture) { name, id -> "Hello $name with id $id" }

vs

val helloFuture: CompletableFuture<String> = nameFuture
        .thenCombineAsync(idFuture, BiFunction { name, id -> "Hello $name with id $id" }, ForkJoinExecutor)

Error Handling

The CompletableFuture API on its error handling callbacks it returns the exception wrapped into a CompletionException, I found this cumbersome to deal with and instead I'm returning the actual cause to all the error handling methods provided.

recover

recover allows you to map an exception into a value you can recover from.

import io.github.vjames19.futures.jdk8.*

val failed = Future<String> { throw IllegalArgumentException() }

val recovered = failed.recover { "recovered" }

val recoveredOnlyWhenYouCanHandleTheException = failed.recover {
    if (it is NoSuchElementException) "recovered"
    else throw it
}

vs

val recoveredOnlyWhenYouCanHandleTheException = failed.exceptionally {
    // unwrap the CompletionException
    val throwable = it.cause ?: it
    
    if (throwable is NoSuchElementException) "recovered"
    else throw throwable
}

recoverWith

recoverWith allows to recover with a new CompletableFuture

import io.github.vjames19.futures.jdk8.*

val failed = Future<String> { throw IllegalArgumentException() }

val recovered = failed.recoverWith { Future { "recovered" } }

val recoveredOnlyWhenYouCanHandleTheException = failed.recoverWith {
    if (it is NoSuchElementException) Future { "recovered" }
    else throw it
    // else it.toCompletableFuture()
}

mapError

mapError allows you only to transform the error types you are interested

import io.github.vjames19.futures.jdk8.*

val failed = Future<String> { throw IllegalArgumentException() }
        .mapError { e: IllegalArgumentException ->
            // handle the IllegalArgumentException here and return a more pertinent exception
        }

fallbackTo

fallbackTo fallbacks to the specified future, in the event that the original future fails

import io.github.vjames19.futures.jdk8.*

val failed = Future<String> { throw IllegalArgumentException() }

val fallbacked = failed.fallbackTo{ Future { "recovered" } }

val success = Future { 10 }

// Keeps the value 10
val keepsTheOriginalValue = success.fallbackTo { Future { 20 } }

Callbacks

onSuccess

onSuccess only gets called when the future is successful

import io.github.vjames19.futures.jdk8.*

val future = Future<String> { TODO() }

val s = future.onSuccess { result -> 
    // do something with the result
}

onFailure

onFailure only gets called when the future fails

import io.github.vjames19.futures.jdk8.*

val future = Future<String> { TODO() }

val s = future.onFailure { throwable -> 
    // do something with the error
}

onComplete

onComplete allows you to register callbacks for both onFailure and onSuccess

import io.github.vjames19.futures.jdk8.*

val future = Future<String> { throw IllegalArgumentException() }

val f = future.onComplete(
        onFailure = { throwable ->
            // do something with the error
        },
        onSuccess = { result -> 
            // do something with the result
        })

vs

val f = future.whenCompleteAsync(BiConsumer({ result, throwable ->
    if (throwable != null) {
        // do something with the error
    } else {
        // do something with the result
    }
}), ForkJoinExecutor)

Tests

In the tests you can find more example as to how to use a given operator.

./gradlew test
Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].