All Downloads are FREE. Search and download functionalities are using the official Maven repository.

.kovenant.kovenant-core.3.3.0.source-code.promises-api.kt Maven / Gradle / Ivy

/*
 * Copyright (c) 2015 Mark Platvoet
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * THE SOFTWARE.
 */
@file:JvmName("KovenantApi")

package nl.komponents.kovenant


/**
 * Deferred is the private part of the [Promise]
 *
 * A Deferred object let's you control a an accompanied [Promise] object
 * by either [resolve] or [reject] it. Any implementation must make sure
 * that a Deferred object can only be completed once as either resolved
 * or rejected.
 *
 * It's up to the implementation what happens when a Deferred gets
 * resolved or rejected multiple times. It may simply be ignored or throw
 * an Exception.
 */
interface Deferred {
    /**
     * Resolves this deferred with the provided value
     *
     * It's up to the implementation what happens when a Deferred gets
     * resolved multiple times. It may simply be ignored or throw
     * an Exception.
     *
     * @param [value] the value to resolve this deferred with
     */
    infix fun resolve(value: V)

    /**
     * Rejects this deferred with the provided error
     *
     * It's up to the implementation what happens when a Deferred gets
     * rejected multiple times. It may simply be ignored or throw
     * an Exception.
     *
     * @param [error] the value to reject this deferred with
     */
    infix fun reject(error: E)

    /**
     * Holds the accompanied [Promise]
     *
     * The accompanied [Promise] for this deferred. Multiple invocations
     * must lead to the same instance of the Promise.
     */
    val promise: Promise
}

/**
 * Resolves a Deferred of type  with Unit.
 * This makes it just a bit more natural looking
 */
fun  Deferred.resolve() = resolve(Unit)

/**
 * Rejects a Deferred of type  with Unit.
 * This makes it just a bit more natural looking
 */
fun  Deferred.reject() = reject(Unit)


/**
 * Mark a class to be cancelable
 *
 * What cancelling exactly means is up to the implementor.
 * But the intention is stopping.
 */
interface CancelablePromise : Promise {
    fun cancel(error: E): Boolean
}


/**
 * A construct for receiving a notification of an asynchronous job
 *
 * A Promise can either resolve in [success] or get rejected and [fail].
 * Either way, it will [always] let you know.
 *
 * Any implementation must ensure that **all** callbacks are offered to their configured DispatcherContext in the order
 * they where added to this Promise.
 */
interface Promise {
    companion object {
        /**
         * Takes any value `V` and wraps it as a successfully resolved promise.
         *
         * @param context the Context associated with the promise
         * @param value the value to wrap into a Promise
         */
        fun  of(value: V, context: Context = Kovenant.context): Promise {
            return concreteSuccessfulPromise(context, value)
        }

        /**
         * Takes any value `V` and wraps it as a successfully resolved promise.
         *
         * @param context the Context associated with the promise
         * @param value the value to wrap into a Promise
         */
        fun  ofSuccess(value: V, context: Context = Kovenant.context): Promise {
            return concreteSuccessfulPromise(context, value)
        }

        /**
         * Takes any value `E` and wraps it as a failed resolved promise.
         *
         * @param context the Context associated with the promise
         * @param value the value to wrap into a failed Promise
         */
        fun  ofFail(value: E, context: Context = Kovenant.context): Promise {
            return concreteFailedPromise(context, value)
        }

    }

    /**
     * The context that is associated with this Promise. By default all callbacks are executed on the callback
     * `DispatcherContext` of this context.
     *
     * Functions like `then`use this to base there returned promises on this to keep promises bound to a
     * desired context.
     */
    val context: Context

    /**
     * Adds a success callback to this Promise
     *
     * Adds a success callback that gets executed on the callbackContext of this promise standard context
     * when this Promise gets successfully resolved.
     *
     * Function added to allow this to be used in infix calls
     *
     * @param callback the callback that gets executed on successful completion
     */
    infix fun success(callback: (value: V) -> Unit): Promise = success(context.callbackContext, callback)

    /**
     * Adds a fail callback to this Promise
     *
     * Adds a fail callback that gets executed on the callbackContext of this promise standard context
     * when this Promise gets rejected with an error or cancelled if this is a [CancelablePromise].
     *
     * Function added to allow this to be used in infix calls
     *
     * @param callback the callback to be executed on failure or cancellation
     */
    infix fun fail(callback: (error: E) -> Unit): Promise = fail(context.callbackContext, callback)

    /**
     * Adds a always callback to this Promise
     *
     * Adds a always callback that gets executed on the callbackContext of this promise standard context
     * when this Promise gets resolved successfully, rejected with an error or cancelled if this is a
     * [CancelablePromise].
     *
     * Function added to allow this to be used in infix calls
     *
     * @param callback the callback to be executed on success, failure or cancellation
     */
    infix fun always(callback: () -> Unit): Promise = always(context.callbackContext, callback)

    /**
     * Adds a success callback to this Promise
     *
     * Adds a success callback that gets executed on the provided callbackContext when this Promise gets successfully
     * resolved.
     *
     * @param callback the callback that gets executed on successful completion
     * @param context the DispatcherContext on which this callback is executed
     */
    fun success(context: DispatcherContext, callback: (value: V) -> Unit): Promise


    /**
     * Adds a fail callback to this Promise
     *
     * Adds a fail callback that gets executed on the provided callbackContext when this Promise gets rejected with an
     * error or cancelled if this is a [CancelablePromise].
     *
     * @param callback the callback to be executed on failure or cancellation
     * @param context the DispatcherContext on which this callback is executed
     */
    fun fail(context: DispatcherContext, callback: (error: E) -> Unit): Promise

    /**
     * Adds a always callback to this Promise
     *
     * Adds a always callback that gets executed on the provided callbackContext when this Promise gets resolved
     * successfully, rejected with an error or cancelled if this is a [CancelablePromise].
     *
     * Function added to allow this to be used in infix calls
     *
     * @param callback the callback to be executed on success, failure or cancellation
     * @param context the DispatcherContext on which this callback is executed
     */
    fun always(context: DispatcherContext, callback: () -> Unit): Promise

    /**
     * Blocks until this promises is done and either immediate returning the success result or throwing an `Exception`
     *
     * Blocks until this promises is done. When this promise is successful this will return success value `V`.
     * When this promise failed this will throw an exception. If the type of `E` is an Exception this will be thrown
     * otherwise a `FailedException` will be thrown with the error value wrapped.
     *
     * @return returns the success value when done
     */
    @Throws(Exception::class) fun get(): V

    /**
     * Blocks until this promises is done and either immediate returning the failure result or throwing a `FailedException`
     *
     * Blocks until this promises is done. When this promise has failed this will return the failure value `E`.
     * When this promise is successful this will throw a `FailedException`.
     *
     * @return returns the fail value when done
     */
    @Throws(FailedException::class) fun getError(): E


    /**
     * Returns true if this promise is either resolved successfully or has failed
     *
     * @return true if this promise is either resolved successfully or has failed, false otherwise
     */
    fun isDone(): Boolean

    /**
     * Returns true if this promise is resolved a failed
     *
     * @return true if this promise is resolved a failed, false otherwise
     */
    fun isFailure(): Boolean

    /**
     * Returns true if this promise is resolved successfully
     *
     * @return true if this promise is resolved successfully, false otherwise
     */
    fun isSuccess(): Boolean
}


/**
 * Creates a new [Deferred] instance.
 *
 * @param context the context on which the associated [Promise] operates on
 * @return newly created [Deferred]
 */
fun  deferred(context: Context = Kovenant.context): Deferred = Kovenant.deferred(context)


/**
 * Creates a new [Deferred] instance with a promise that is a [CancelablePromise]
 *
 * @param context the context on which the associated [Promise] operates on
 * @param onCancelled called when the [Promise] has been cancelled
 * @return newly created [Deferred]
 */
fun  deferred(context: Context = Kovenant.context, onCancelled: (E) -> Unit): Deferred = Kovenant.deferred(context, onCancelled)


/**
 * Executes the given task on the work [DispatcherContext] of provided [Context] and returns a [Promise].
 * Any Ecxeption is considered a failure.
 *
 * @param body the task to be executed
 * @param context the context on which the task is executed and the [Promise] is tied to. `Kovenant.context` by default.
 * @return returns a [Promise] of inferred success type [V] and failure type [Exception]
 */
@Deprecated("async is a keyword, favor task instead", ReplaceWith("task(context, body)")) fun  async(context: Context = Kovenant.context,
                                                                                                        body: () -> V): Promise = task(context, body)

/**
 * Executes the given task on the work [DispatcherContext] of provided [Context] and returns a [Promise].
 * Any Exception is considered a failure.
 *
 * @param body the task to be executed
 * @param context the context on which the task is executed and the [Promise] is tied to. `Kovenant.context` by default.
 * @return returns a [Promise] of inferred success type [V] and failure type [Exception]
 */
@JvmOverloads fun  task(context: Context = Kovenant.context,
                           body: () -> V): Promise = concretePromise(context, body)

/**
 * Asynchronously bind the success value of a [Promise] and returns a new [Promise] with the transformed value.
 *
 * Transforms Promise A to Promise B. If Promise A resolves successful then [bind] is executed on the
 * work [DispatcherContext] of the default `Kovenant.context` and returns Promise B. If [bind] is successful,
 * meaning now Exception is thrown then Promise B resolves successful, failed otherwise.
 *
 * If Promise A fails with error E, Promise B will fail with error E too.
 *
 *
 * @param bind the transform function.
 */
infix fun  Promise.then(bind: (V) -> R): Promise {
    return concretePromise(context, this, bind)
}

/**
 * Asynchronously bind the success value of a [Promise] and returns a new [Promise] with the transformed value.
 *
 * Transforms Promise A to Promise B. If Promise A resolves successful then [bind] is executed on the
 * work [DispatcherContext] of the default `Kovenant.context` and returns Promise B. If [bind] is successful,
 * meaning now Exception is thrown then Promise B resolves successful, failed otherwise.
 *
 * If Promise A fails with error E, Promise B will fail with error E too.
 *
 * @param context the on which the bind and returned Promise operate
 * @param bind the transform function.
 */
fun  Promise.then(context: Context, bind: (V) -> R): Promise {
    return concretePromise(context, this, bind)
}

/**
 * Asynchronously bind the success value of a [Promise] and returns a new [Promise] with the transformed value.
 *
 * Transforms Promise A to Promise B. If Promise A resolves successful then [bind] is executed on the
 * work [DispatcherContext] of the default `Kovenant.context` and returns Promise B. If [bind] is successful,
 * meaning now Exception is thrown then Promise B resolves successful, failed otherwise.
 *
 * If Promise A fails with error E, Promise B will fail with error E too.
 *
 *
 * @param bind the transform function.
 */
infix inline fun  Promise.thenApply(
        crossinline bind: V.() -> R): Promise = then { it.bind() }


@Deprecated("renamed to 'thenApply'", ReplaceWith("thenApply(bind)"))
infix inline fun  Promise.thenUse(
        crossinline bind: V.() -> R): Promise = then { it.bind() }

/**
 * Transforms any `Promise` into a Promise.
 *
 * The purpose is to hide any result from the consumer but still give the ability to know when something is ready.
 *
 * @return returns the Promise with both value and error hidden
 */
fun  Promise.toVoid(context: Context = this.context): Promise {
    if (isDone()) {
        if (isSuccess()) return Promise.ofSuccess(Unit, context)
        if (isFailure()) return Promise.ofFail(Unit, context)
    }

    val deferred = deferred(context)
    success { deferred.resolve() }
    fail { deferred.reject() }
    return deferred.promise
}

/**
 * Transforms any `Promise` into a Promise.
 *
 * The purpose is to hide any result from the consumer but still give the ability to know when something is ready.
 * Hides the error only
 *
 * @return returns the Promise with the error hidden
 */
fun  Promise.toFailVoid(context: Context = this.context): Promise {
    if (isDone()) {
        if (isSuccess()) return Promise.ofSuccess(get(), context)
        if (isFailure()) return Promise.ofFail(Unit, context)
    }

    val deferred = deferred(context)
    success { deferred.resolve(it) }
    fail { deferred.reject() }
    return deferred.promise
}

/**
 * Transforms any `Promise` into a Promise.
 *
 * The purpose is to hide any result from the consumer but still give the ability to know when something is ready.
 * Hides the value only.
 *
 * @return returns the Promise with the value hidden
 */
fun  Promise.toSuccessVoid(context: Context = this.context): Promise {
    if (isDone()) {
        if (isSuccess()) return Promise.ofSuccess(Unit, context)
        if (isFailure()) return Promise.ofFail(getError(), context)
    }

    val deferred = deferred(context)
    success { deferred.resolve() }
    fail { deferred.reject(it) }
    return deferred.promise
}


/**
 * Unwraps any nested Promise.
 *
 * Unwraps any nested Promise. By default the returned `Promise` will operate on the same `Context` as its parent
 * `Promise`, no matter what the `Context` of the nested `Promise` is. If you want the resulting promise to operate on
 * a different `Context` you can provide one.
 *
 * Function tries to be as efficient as possible in cases where this or the nested `Promise` is already resolved. This
 * means that this function might or might not create a new `Promise`, it all depends on the current state.
 *
 * @param context the `Context` on which the returned `Promise` operates
 * @return the unwrapped Promise
 */
fun  Promise, E>.unwrap(context: Context = this.context): Promise {
    if (isDone()) when {
        isSuccess() -> return get().withContext(context)
        isFailure() -> return Promise.ofFail(getError(), context)
    }

    val deferred = deferred(context)
    success {
        nested ->
        nested success { deferred resolve it }
        nested fail { deferred reject it }
    } fail {
        deferred reject it
    }
    return deferred.promise
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy