JavaPromise is a lightweight Promise library based on the A+ promise spec and inspired by PromiseKit and Bluebird. All modern day data-driven apps deal with asynchronous programming, this causes challenges and can lead to callback hell. The use of anonymous inner classes for callbacks in Java can make code even more unorganized and hard to follow. Using promises to handle asynchronous processes can help you make your code more concise, organized and efficient by offering a cleaner API and eliminating the need for duplicating error handling logic.
makeAsyncRequest()
.then(data -> {
return transformData(data);
})
.<CustomViewModel>then(transformedData -> {
// transformedData is an instanceof CustomViewModel
mTextView.setText(transformedData.title);
})
.error(error -> {
// handle failure of either async request or transformData
})
Add the following to your project build.gradle
file:
allprojects {
repositories {
jcenter()
maven {
url "http://dl.bintray.com/nascent/Maven"
}
}
}
Then add this to your module's build.gradle
file:
dependencies {
compile 'com.javapromise.promise:javapromiseandroid:0.0.4'
}
Sync your gradle project and your good to go!
You can create a promise by wrapping an existing asynchronous process
public Promise<String> getGooglePage() {
RequestQueue queue = Volley.newRequestQueue(this);
return new Promise(deferral -> {
StringRequest stringRequest = new StringRequest(Request.Method.GET,
"http://www.google.com", new Response.Listener<String>() {
@Override
public void onResponse(String response) {
// Resolve with the response
deferral.resolve(response);
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
// fail with exception
deferral.reject(new Exception(error.message));
}
});
// Add the request to the RequestQueue.
queue.add(stringRequest);
})
}
getGooglePage()
.then(response -> {
// this will execute when the initial promise is resolved
// value in this case will be strongly typed as a string
assert(value instanceof String) // true
myTextView.setText(Html.fromHtml(response));
}).error(error -> {
// this will execute if the inital promise is rejected
});
There are convenience methods for creating rejected and resolved Promises:
Promise.resolve(value);
Promise.reject(value);
Chaining promises can be accomplished by either returning a value or promise in a then
block.
Unfortunately due to limitations of generics in Java you are required to provide the type info for any
chained then
blocks after the first.
somePromise
.then(v1 -> {
// v1 will be strongly typed
return Integer.valueOf(50);
})
.<Integer>then(v2 -> {
// v2 will be 50, as of right now the type of the value in the
// 2nd chained then needs to be explicit.
return someMethodThatReturnsStringPromise(v2);
})
.<String>then(v3 -> {
// this block will execute once the promise returned above is resolved
assert(v3 instanceof String)
})
.error(error -> {
// this error block will be executed if any of the above promises is rejected
// unless one of those rejections is already handled by a .error block and that
// exception is not re-thrown
})
.always(() -> {
// this block will be executed regardless of resolution/rejection
});
If an exception is thrown in a then
block, the return value is a rejected Promise.
promise.then(value -> {
throw new RunTimeException("Unexpected");
}).then(v2 -> {
// this block won't execute
}).error(error -> {
// this block will execute where error is the exception thrown above
// Note: you are able to re-throw this exception if you do not want it
// to be consumed by this error block
});
Rejected promises can be 'recovered' in a error block that will return a resolved promise.
Promise.<String>reject(new Exception("Failure"))
.error((error, recovery) -> {
// invoking recovery.recover with a value matching the original type specified
// will result in a resolved promise
recovery.recover(String.valueOf("Recovered"));
})
.<String>then(value -> {
assert(value.equals("Recovered"));
})
You can wait on multiple promises by providing an arraylist or array of Promises that represent the same value type to Promise.when
.
Integer[] initialValues = { 0, 1, 2, 3 };
ArrayList<Promise<Integer>> promises = new ArrayList<>();
for (Integer num : initialValues) {
promises.add(createPromise(num));
}
Promise.when(promises)
.then(values -> {
// where values is an arraylist of Integers matching the order of
// the promises arraylist
});
If you need to wait on a list of promises that represent varying types (value list will not be strongly typed) you can use Promise.all
.
Promise[] list = {
Promise.resolve(String.valueOf("Test")),
Promise.resolve(Integer.valueOf(20)),
Promise.resolve(new ArrayList()),
Promise.resolve(someCustomObject)
};
Promise.all(list)
.then(valueList -> {
// valueList will be an ArrayList of values matching the order
// of the promises in the input list
// they have all be of type Object
return null;
});
You can wait on up to three promises of differing types using Promise.when
in combination with VariadicPromiseValue
.
Using this strategy will give you strongly typed results.
String firstVal = "First value";
Integer secondVal = new Integer(50);
Promise<VariadicPromiseValue<String, Integer, Void>> promise = Promise.when(
createAsyncPromise(firstVal),
createAsyncPromise(secondVal)
);
promise
.then(values -> {
values.getFirst() // "First value"
values.getSecond() // 50
values.getThird() // null
})
Run
$ scripts/setup.sh
To set up pre-commit symlinks. Pre-commit hooks will run unit tests and increment the version number by 0.0.1
.
Running git commit
with the --no-verify
will skip the pre-commit hooks.
$ ./gradlew install
$ ./gradlew bintrayUpload
https://antoniocappiello.com/2015/11/11/publish-your-library-to-jcenter-in-3-steps/
- add username and API key to local.properties file
- README:
- install instructions without gradle
- COMING SOON Features:
- tap