com.codename1.util.promise.Promise is an implementation of the JavaScript promise for Codename One. It exists to compose asynchronous work without nesting a callback inside a callback inside a callback: each step is a value you can hand around, chain onto and combine with others.

Two method names differ from the JavaScript originals, because catch and finally are Java keywords. except takes the place of catch. always runs for either outcome, but it isn’t a finally: it’s implemented as then(f, f), so whatever the functor returns becomes the value of the promise always hands back. Returning null after a rejection resolves the rest of the chain instead of keeping it rejected, and returning null after a success replaces the value. Returning the incoming value keeps a successful chain unchanged, but it doesn’t preserve a rejection: any non-promise return is treated as a result, so returning the Throwable fulfils the chain with it. To stay rejected you have to throw, because processThens catches what a functor throws and rejects with it — but Functor.call declares no checked exceptions, so a checked reason can’t simply be rethrown. Wrap it, as throw new RuntimeException(err), or return a promise you rejected with the original reason, whose state the chain adopts.

Creating and consuming one

The constructor takes an ExecutorFunction, which receives the two functions that settle the promise. It runs immediately, on the calling thread:

Promise<String> greeting = new Promise<>((resolve, reject) -> {
    // called once, on whatever thread you start the work from
    Log.p("working");
    resolve.call("hello");
});

// ready() attaches both callbacks to this promise. Chaining
// onSuccess().onFail() instead would attach the failure handler to the
// promise onSuccess returns, which has already put the default casting
// rejection functor in the way
greeting.ready(
        value -> Log.p(value),
        err -> Log.e(err));

Resolution is marshalled for you. resolve and reject check whether they’re on the EDT and hand themselves to callSerially when they aren’t, so the callbacks you attach always run on the EDT and can touch the UI directly. That is worth contrasting with EasyThread, whose result callback stays on the worker thread.

Chaining

There are two families of callback and the difference is what they return. then, except and always take a Functor, whose return value becomes the input of the next step — that’s what makes a chain a pipeline. onSuccess, onFail and onComplete take a SuccessCallback instead, which returns nothing, so they suit the end of a chain where there is no value to pass on. Attach both outcomes with ready(success, failure) rather than chaining onSuccess(…​).onFail(…​): the second form hangs the failure handler off the promise onSuccess returns, one link further down the chain than the promise whose failure you meant to catch:

// then() pipes the return value of one step into the next, so each
// Functor has to return something
Promise<String> name = Promise.resolve("ada");
name.then(
        n -> ((String)n).toUpperCase(),
        // a rejection functor is optional -- omit it and the reason
        // passes through untouched to the next handler. Supply one, as
        // here, and it has to rethrow: anything RETURNED from it
        // RESOLVES the rest of the chain, so returning null would run
        // the steps below on a failure. Functor.call declares no
        // checked exception, hence the wrap
        err -> { throw new RuntimeException((Throwable)err); })
    .then(upper -> "Hello " + upper)
    .ready(
        msg -> Log.p((String)msg),
        err -> Log.e((Throwable)err));

getState() reports Pending, Fulfilled or Rejected, and getValue() returns the resolved value once there is one.

A then given only a success functor passes a rejection straight through to the promise it returns, carrying the original reason, so an IOException from a network call arrives at your except as that IOException. The success functor is skipped for a rejected promise, which is why a chain can end in a single except rather than handling failure at every link.

Combining several

all and allSettled both take a varargs list of promises and return one promise, and they differ in how they treat a failure:

Promise<String> a = Promise.resolve("one");
Promise<String> b = Promise.resolve("two");

// all() rejects the moment any input rejects
Promise.all(a, b).onSuccess(values -> Log.p("both arrived"));

// allSettled() waits for every input whatever the outcome
Promise.allSettled(a, b).onSuccess(results -> Log.p("all finished"));

all rejects as soon as any input rejects, so it fits work where one failure makes the rest pointless. Don’t depend on which error you get when several inputs fail. A handler attached before the rejections arrive is drained by the first one, while a handler attached afterward reads the stored error, which each later rejection overwrites. allSettled waits for every input whatever happens to each, which fits independent tasks. It resolves with the original array of promises rather than a list of outcome descriptors, so inspect each one with getState() and getValue(). There’s no getter for a rejected promise’s error, so attach a failure handler to an input if you need to see why it failed.

Working with AsyncResource

Much of the framework returns AsyncResource rather than a promise, and the conversion goes both ways:

// an AsyncResource-returning API becomes a Promise
Promise<String> p = Promise.promisify(asyncCall());

// and back again, for an API that expects AsyncResource
com.codename1.util.AsyncResource<String> back = p.asAsyncResource();

// await() blocks with invokeAndBlock, so it is safe on the EDT; it
// throws AsyncResource.AsyncExecutionException if the promise rejected
String value = p.await();
Log.p(value + back.getClass().getName());

await() is the blocking form. It waits through invokeAndBlock, so calling it on the EDT doesn’t freeze the UI, and it throws AsyncResource.AsyncExecutionException when the promise rejected.

A promise from promisify never settles if the underlying AsyncResource is cancelled after promisify registered on it. AsyncResource.cancel records the cancellation and notifies its observers without running that callback, so the promise stays Pending and an await() waits forever. A resource already cancelled beforehand is fine: registering on a resource that has finished with an error fires the callback straight away. Don’t await a promise wrapping a resource something else can cancel.