├── .gitignore
├── README.md
├── build.gradle
├── gradle.properties
├── gradle
├── sonatype.gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── rxjava-promises
├── README.md
├── build.gradle
├── rxjava-promises-core
│ ├── build.gradle
│ └── src
│ │ └── main
│ │ └── java
│ │ └── com
│ │ └── darylteo
│ │ └── rx
│ │ └── promises
│ │ └── AbstractPromise.java
├── rxjava-promises-groovy
│ ├── build.gradle
│ └── src
│ │ ├── main
│ │ └── groovy
│ │ │ └── com
│ │ │ └── darylteo
│ │ │ └── rx
│ │ │ └── promises
│ │ │ └── groovy
│ │ │ └── Promise.groovy
│ │ └── test
│ │ └── groovy
│ │ └── com
│ │ └── darylteo
│ │ └── rx
│ │ └── promises
│ │ └── groovy
│ │ └── tests
│ │ └── PromisesTestGroovy.groovy
└── rxjava-promises-java
│ └── src
│ ├── main
│ └── java
│ │ └── com
│ │ └── darylteo
│ │ └── rx
│ │ └── promises
│ │ └── java
│ │ ├── Promise.java
│ │ └── functions
│ │ ├── FinallyAction.java
│ │ ├── FinallyFunction.java
│ │ ├── PromiseAction.java
│ │ ├── PromiseFunction.java
│ │ └── RepromiseFunction.java
│ └── test
│ └── java
│ └── com
│ └── darylteo
│ └── rx
│ └── promises
│ └── test
│ ├── PromiseRxJavaTests.java
│ └── PromiseTestsJava.java
├── settings.gradle
└── vertx-promises
├── README.md
├── build.gradle
├── vertx-promises-groovy
├── build.gradle
└── src
│ └── main
│ └── groovy
│ └── com
│ └── darylteo
│ └── vertx
│ └── promises
│ └── groovy
│ └── Promise.groovy
└── vertx-promises-java
├── .cache
├── build.gradle
└── src
├── main
└── java
│ └── com
│ └── darylteo
│ └── vertx
│ └── promises
│ └── java
│ ├── Promise.java
│ └── functions
│ ├── FinallyAction.java
│ ├── FinallyFunction.java
│ ├── PromiseAction.java
│ ├── PromiseFunction.java
│ └── RepromiseFunction.java
└── test
└── java
└── com
└── darylteo
└── vertx
└── promises
└── java
└── test
└── PromiseTests.java
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | .gradle
3 | .idea
4 | .classpath
5 | .project
6 | .settings
7 | .yardoc
8 | .yardopts
9 | .tmp
10 | bin
11 | build
12 | target
13 | out
14 | *.iml
15 | *.ipr
16 | *.iws
17 | test-output
18 | Scratch.java
19 | ScratchTest.java
20 | test-results
21 | test-tmp
22 | *.class
23 |
24 | mods
25 | userHome
26 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # RxJava-Promises #
2 |
3 | Promises library for JVM based languages
4 |
5 | ## IMPORTANT NOTE ##
6 |
7 | Hi all! Unfortunately, this library was built at a stage where RxJava was not very mature, and my experience in OSS was quite poor, so here are some questions frequently asked.
8 |
9 | ### Are you still maintaining this? ###
10 |
11 | No I am not. However, many employers still ask for your GitHub profile to show some sort of self-driven initiative, so I leave this repository here publicly. If you have any need for changes, please fork.
12 |
13 | ### Talking about Forks - what license is this under? ###
14 |
15 | Yes, unfortunately, I had copied a Gradle template originally. Therefore, the artifacts being produced are indicated as being Apache licensed.
16 |
17 | Please ignore it. For all source in this repository, everything is "do whatever you want with it", and provided as is.
18 |
19 | ### Do you recommend anything else? ###
20 |
21 | Yes! I have recently found RxJava to be highly adequate for more general use cases where one would have previously preferred a Promise. A PublishSubject is essentially what you want.
22 |
23 | ## What is it? ##
24 |
25 | Promises is a pattern for callback-based flow control. This particular implementation of Promises is useful
26 | in places where blocking calls are undesired.
27 |
28 | ## Where to Start
29 |
30 | - If you wish to use RxJava-Promises as a standalone library, see [RxJava-Promises](rxjava-promises/).
31 | - If you wish to use RxJava-Promises as a vert.x Module, see [Vertx-Promises](vertx-promises/).
32 |
33 | ## Notes ##
34 |
35 | ### Releases
36 |
37 | Core v1.1.2 - As of core v1.1.2, jars are targetted at Java 1.6 to support Android. See [#2](../../issues/2)
38 | Core v1.2.0 - Removes inheritance from Observable<>. Updated to rxjava 0.19.6.
39 |
40 | ### Versioning Information
41 | As there is a close dependency between the two subset of projects, the versioning strategy will be as follows:
42 |
43 | - both groups of projects will always use the same major and minor versions.
44 | - revision numbers will remain individual to each group. For example:
45 | a release of *vertx-promises:X.Y.?* will always use *rxjava-promises.core:X.Y.?* in its dependencies.
46 |
47 | ### Technical Details ###
48 | This library implements **most** of the [Promises/A+](http://promises-aplus.github.io/promises-spec/) spec.
49 | It is also based on [Q](https://github.com/kriskowal/q) for Node.JS, which adds additional conveniences such
50 | as reject and finally. Finally, it is built using the [RxJava](https://github.com/Netflix/RxJava) library.
51 |
52 | This library is unlike other similar language based implementations (such as Futures, or Groovy Promise)
53 | as it is completely unblocking. It is designed to work with asynchronous-callback-heavy platforms.
54 | Furthermore, it has several additional classes that aims to improve the type-safety of Java-based usage,
55 | while minimising the verbosity by generics inferencing.
56 |
57 | ### Vert.x ###
58 |
59 | The primary motivation for this project is to provide a more convenient means of callback flow-control for the
60 | [vert.x](http://github.com/eclipse/vert.x) platform. However, I decided that it would be nice if this can be
61 | used in other places as well. Therefore, the project contains two separate groups of subprojects.
62 |
63 | ## Future Work ##
64 |
65 | ### rx.Observable<> Limitations
66 |
67 | While built on the RxJava library, it currently does not fully support the polyglot nature of the library as
68 | it is still in flux. Once that work has stabilised, a working implementation can be released.
69 |
70 | ### Promises/A+ spec
71 |
72 | This implementation tries its best to fulfill all the points proposed in the Promises/A+ spec, but is still
73 | lacking in some areas. Work will continue to improve its conformance with the spec.
74 |
75 | ### More languages
76 |
77 | Only Java and Groovy are supported currently. More languages should come (if interest is high).
78 |
79 | ### Overall Documentation and Testing Quality
80 |
81 | This code-base is currently lacking in this area, and I hope I will improve this thoroughly in the future.
82 |
83 | ## Documentation ##
84 |
85 | View the README.md in each project for documentation.
86 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | repositories {
3 | mavenCentral()
4 | mavenLocal()
5 | }
6 |
7 | dependencies { classpath "com.darylteo.vertx:vertx-gradle-plugin:${vertxPluginVersion}" }
8 | }
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | /* Vertx */
2 | vertxVersion=2.0.2-final
3 | vertxPluginVersion=0.1.2
4 |
5 | junitVersion=4.11
--------------------------------------------------------------------------------
/gradle/sonatype.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'maven'
2 | apply plugin: 'signing'
3 |
4 | def release = { !project.version.endsWith('-SNAPSHOT') }
5 | def sonatypeUsername = { project.hasProperty('sonatypeUsername') ? project.sonatypeUsername : '' }
6 | def sonatypePassword = { project.hasProperty('sonatypePassword') ? project.sonatypePassword : '' }
7 |
8 | configurations { archives }
9 |
10 | task sourcesJar(type: Jar) {
11 | from sourceSets.main.allSource
12 | classifier = 'sources'
13 | }
14 |
15 | task javadocJar(type: Jar) {
16 | from javadoc
17 | classifier = 'javadoc'
18 | }
19 |
20 | artifacts {
21 | archives sourcesJar
22 | archives javadocJar
23 | }
24 |
25 | signing {
26 | required { release() && gradle.taskGraph.hasTask(uploadArchives) }
27 | sign configurations.archives
28 | }
29 |
30 | uploadArchives {
31 | repositories {
32 | mavenDeployer {
33 | afterEvaluate {
34 | def url = release() ? 'https://oss.sonatype.org/service/local/staging/deploy/maven2/' : 'https://oss.sonatype.org/content/repositories/snapshots/'
35 |
36 | repository(url: url) {
37 | authentication(userName: sonatypeUsername(), password: sonatypePassword())
38 | }
39 | }
40 |
41 | uniqueVersion = false
42 |
43 | beforeDeployment { dep ->
44 | signing.signPom(dep)
45 | }
46 |
47 | configuration = configurations.archives
48 | }
49 | }
50 | }
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/darylteo/rxjava-promises/c528e8e571b88b54653ab9eb39f9626e89ba9de9/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Sun Apr 06 21:00:28 EST 2014
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=http\://services.gradle.org/distributions/gradle-1.11-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # For Cygwin, ensure paths are in UNIX format before anything is touched.
46 | if $cygwin ; then
47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
48 | fi
49 |
50 | # Attempt to set APP_HOME
51 | # Resolve links: $0 may be a link
52 | PRG="$0"
53 | # Need this for relative symlinks.
54 | while [ -h "$PRG" ] ; do
55 | ls=`ls -ld "$PRG"`
56 | link=`expr "$ls" : '.*-> \(.*\)$'`
57 | if expr "$link" : '/.*' > /dev/null; then
58 | PRG="$link"
59 | else
60 | PRG=`dirname "$PRG"`"/$link"
61 | fi
62 | done
63 | SAVED="`pwd`"
64 | cd "`dirname \"$PRG\"`/" >&-
65 | APP_HOME="`pwd -P`"
66 | cd "$SAVED" >&-
67 |
68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
69 |
70 | # Determine the Java command to use to start the JVM.
71 | if [ -n "$JAVA_HOME" ] ; then
72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
73 | # IBM's JDK on AIX uses strange locations for the executables
74 | JAVACMD="$JAVA_HOME/jre/sh/java"
75 | else
76 | JAVACMD="$JAVA_HOME/bin/java"
77 | fi
78 | if [ ! -x "$JAVACMD" ] ; then
79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
80 |
81 | Please set the JAVA_HOME variable in your environment to match the
82 | location of your Java installation."
83 | fi
84 | else
85 | JAVACMD="java"
86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
87 |
88 | Please set the JAVA_HOME variable in your environment to match the
89 | location of your Java installation."
90 | fi
91 |
92 | # Increase the maximum file descriptors if we can.
93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
94 | MAX_FD_LIMIT=`ulimit -H -n`
95 | if [ $? -eq 0 ] ; then
96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
97 | MAX_FD="$MAX_FD_LIMIT"
98 | fi
99 | ulimit -n $MAX_FD
100 | if [ $? -ne 0 ] ; then
101 | warn "Could not set maximum file descriptor limit: $MAX_FD"
102 | fi
103 | else
104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
105 | fi
106 | fi
107 |
108 | # For Darwin, add options to specify how the application appears in the dock
109 | if $darwin; then
110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
111 | fi
112 |
113 | # For Cygwin, switch paths to Windows format before running java
114 | if $cygwin ; then
115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
158 | function splitJvmOpts() {
159 | JVM_OPTS=("$@")
160 | }
161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
163 |
164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
165 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/rxjava-promises/README.md:
--------------------------------------------------------------------------------
1 | # RxJava-Promises #
2 |
3 | ## Maven
4 |
5 | ```XML
6 |
7 | com.darylteo
8 | rxjava-promises-{lang}
9 | 1.2.0
10 |
11 | ````
12 |
13 | ## Gradle
14 |
15 | compile 'com.darylteo:rxjava-promises-java:1.2.0'
16 |
17 |
18 | ## Documentation
19 |
20 | ### Creating a Promise
21 |
22 | Either use the constructor or the defer() static method
23 |
24 | ```java
25 | Promise p1 = new Promise();
26 | Promise p2 = new Promise<>();
27 | ````
28 |
29 | ### then()
30 |
31 | To add a new action to a promise that is invoked when it is fulfilled, you may use the then(onFulfilled) function.
32 |
33 | ```java
34 | p1.then(new PromiseAction() {
35 | public void call(String value) {
36 | // do something with String value
37 | }
38 | });
39 |
40 | p1.then(new PromiseFunction() {
41 | public String call(String value) {
42 | // do something with String value and return a String
43 | return value.toUpperCase();
44 | }
45 | });
46 |
47 | p1.then(new RepromiseFunction() {
48 | public Promise call(String value) {
49 | // do something with String value and return another Promise for a String
50 | Promise result = new Promise():
51 | ...
52 | return result;
53 | }
54 | });
55 | ````
56 |
57 | To detect when a promise is rejected (or an error occurs), using then(onFulfilled, onRejected).
58 |
59 | ```java
60 | p1.then(new PromiseAction() {
61 | public void call(String value) {
62 | // do something with String value
63 | }
64 | }, new PromiseAction() {
65 | public void call(Exception reason) {
66 | // recover from the error
67 | }
68 | });
69 | ````
70 |
71 | ### fail()
72 |
73 | As a convenience, you may use fail(onRejected).
74 |
75 | ```java
76 | p1.fail(new PromiseAction() {
77 | public void call(Exception reason) {
78 | // recover from the error
79 | }
80 | });
81 | ````
82 |
83 | ### finally
84 |
85 | Finally is a non-spec feature for promises. It is invoked immediately after the promise is fulfilled
86 | or rejected. Just call fin(onFinally).
87 |
88 | ```java
89 | p1.fin(new FinallyAction() {
90 | public void call() {
91 | // cleanup
92 | }
93 | });
94 |
95 | p1.fin(new FinallyFunction() {
96 | public Promise call() {
97 | // returning a promise will delay the chain of promises until it is fulfilled
98 | return new Promise();
99 | }
100 | });
101 | ````
102 |
103 | ### fulfill() / reject()
104 |
105 | Fulfilling or Rejecting a promise is straightforward.
106 |
107 | ```java
108 | // fulfilling a promise
109 | p1.fulfill("So Long");
110 |
111 | // rejecting
112 | p1.reject("And thanks for all the fish.");
113 | ````
114 |
115 | ## Implementing a different Language
116 |
117 | To implement Promises in a JVM Language of your choice, you should write a subclass implementation of
118 | com.darylteo.rx.promises.AbstractPromise. Implement the 3 functions (and any overloads) as required:
119 |
120 | - then
121 | - fail
122 | - finally
123 |
124 | View an example in the
125 | [java](java/src/main/java/com/darylteo/rx/promises/java/Promise.java)
126 | and [groovy](groovy/src/main/groovy/com/darylteo/rx/promises/groovy/Promise.groovy)
127 | implementations.
128 |
129 | ## Java vs Other Languages
130 |
131 | ### Java
132 | As you can see from the code above, while Promises offers a solution to "callback hell", its usage in Java
133 | is still rather verbose, and due to the type-safe nature of Java, there are a couple of rules that must be followed:
134 |
135 | - when using both onFulfilled and onRejected, you must always return the same return-type as its handler will
136 | be expecting a single class. If, for some reason, you absolutely must allow for different return types, you
137 | should then use a container class, or a common superclass.
138 | - if calling fail(), you may not change the return type. The fulfilled value will be passed through the chain
139 | which will still be expecting the same data type.
140 |
141 | There are also several handler implementations available for convenience to Java users.
142 |
143 | ### Other Languages
144 |
145 | If you are using a different language implementation (for example, Groovy), you will be able to sacrifice
146 | the type-safety of your code to avoid that limitation. The amount of convenience that is awarded is determined by:
147 |
148 | - support for dynamic typing
149 | - "first-class" functions (or closures, lambdas, delegates etc.)
150 |
151 | For example, in Groovy, your code should look more like this:
152 |
153 | ```groovy
154 | aPromiseReturningFunction()
155 | .then { result ->
156 | return result.toUpperCase()
157 | }
158 | .then { result ->
159 | return result[6..11]
160 | }
161 | .fail { error ->
162 | return 'World'
163 | }
164 | .then { result ->
165 | println result
166 | }
167 | ````
168 |
169 | Keep your ears open for new language implementations in the future!
170 |
--------------------------------------------------------------------------------
/rxjava-promises/build.gradle:
--------------------------------------------------------------------------------
1 | subprojects {
2 | apply plugin: 'java'
3 | apply plugin: 'eclipse'
4 | apply from: rootProject.file('gradle/sonatype.gradle')
5 |
6 | repositories {
7 | mavenCentral()
8 | }
9 | dependencies {
10 | testCompile 'junit:junit:4.11'
11 | }
12 |
13 | // Target Java 6 for Android users!
14 | sourceCompatibility = '1.6'
15 |
16 | group = 'com.darylteo'
17 | version = '1.2.1'
18 |
19 | uploadArchives {
20 | repositories {
21 | mavenDeployer {
22 | pom {
23 | project {
24 | name "RxJava Promises - ${project.name}"
25 | description "Promises implementation for RxJava - ${project.name}"
26 | inceptionYear '2013'
27 | url 'http://github.com/darylteo/rxjava-promises'
28 |
29 | developers {
30 | developer {
31 | id 'darylteo'
32 | name 'Daryl Teo'
33 | email 'i.am@darylteo.com'
34 | }
35 | }
36 |
37 | scm { url 'http://github.com/darylteo/rxjava-promises' }
38 |
39 | licenses {
40 | license {
41 | name 'The Apache Software License, Version 2.0'
42 | url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
43 | distribution 'repo'
44 | }
45 | }
46 |
47 | properties {
48 | 'project.build.sourceEncoding'('UTF8')
49 | }
50 | }
51 | }
52 | }
53 | }
54 | }
55 |
56 | }
57 |
58 | configure(subprojects - project(':rxjava-promises:rxjava-promises-core')) {
59 | dependencies { compile project(':rxjava-promises:rxjava-promises-core') }
60 | }
61 |
--------------------------------------------------------------------------------
/rxjava-promises/rxjava-promises-core/build.gradle:
--------------------------------------------------------------------------------
1 | dependencies {
2 | compile "com.netflix.rxjava:rxjava-core:0.19.6"
3 | }
4 |
--------------------------------------------------------------------------------
/rxjava-promises/rxjava-promises-core/src/main/java/com/darylteo/rx/promises/AbstractPromise.java:
--------------------------------------------------------------------------------
1 | package com.darylteo.rx.promises;
2 |
3 | import rx.Observable;
4 | import rx.Observer;
5 | import rx.exceptions.OnErrorThrowable;
6 | import rx.functions.*;
7 | import rx.subjects.ReplaySubject;
8 |
9 | public abstract class AbstractPromise implements Observer {
10 | public static enum STATE {
11 | PENDING,
12 | FULFILLED,
13 | REJECTED
14 | }
15 |
16 | /* Properties */
17 | private AbstractPromise that = this;
18 |
19 | private ReplaySubject subject;
20 | private Observable obs;
21 |
22 | private STATE state = STATE.PENDING;
23 | private T value = null;
24 | private Throwable reason;
25 |
26 | public STATE getState() {
27 | return this.state;
28 | }
29 |
30 | public boolean isPending() {
31 | return this.state == STATE.PENDING;
32 | }
33 |
34 | public boolean isFulfilled() {
35 | return this.state == STATE.FULFILLED;
36 | }
37 |
38 | public boolean isRejected() {
39 | return this.state == STATE.REJECTED;
40 | }
41 |
42 | public T getValue() {
43 | return this.value;
44 | }
45 |
46 | public Throwable getReason() {
47 | return this.reason;
48 | }
49 |
50 | /* Constructor */
51 | public AbstractPromise() {
52 | this(null);
53 | }
54 |
55 | public AbstractPromise(Observable source) {
56 | this.subject = ReplaySubject.create();
57 | this.obs = this.subject.last();
58 |
59 | if (source != null) {
60 | source.subscribe(this.subject);
61 | }
62 |
63 | // promise states
64 | this.obs.subscribe(new Observer() {
65 | @Override
66 | public void onCompleted() {
67 | that.state = STATE.FULFILLED;
68 | }
69 |
70 | @Override
71 | public void onError(Throwable reason) {
72 | that.state = STATE.REJECTED;
73 | that.reason = reason;
74 | }
75 |
76 | @Override
77 | public void onNext(T value) {
78 | that.value = value;
79 | }
80 | });
81 | }
82 |
83 | /* ================== */
84 | /* Main Defer Function */
85 | protected AbstractPromise _then(
86 | final Function onFulfilled,
87 | final Function onRejected,
88 | final Function onFinally) {
89 | // This is the next promise in the chain.
90 | // The handlers you see below will resolve their values and forward them
91 | // to this promise.
92 | final AbstractPromise deferred = this._create();
93 |
94 | // Create the Observer
95 | final Observer observer = new Observer() {
96 | @Override
97 | public void onCompleted() {
98 | this.evaluate();
99 | }
100 |
101 | @Override
102 | public void onError(Throwable reason) {
103 | this.evaluate();
104 | }
105 |
106 | @Override
107 | public void onNext(T value) {
108 |
109 | }
110 |
111 | private void evaluate() {
112 | try {
113 | // onfinally and onFulfilled/onRejected are mutually exclusive
114 | // note: this implementation of finally is closer to "onComplete" rather than "finallyDo"
115 | // in that it fires immediately when the previous Promise is fulfilled, rather than
116 | // for the entire sequence of Observables to complete its sequence.
117 | if (onFinally != null) {
118 | evaluateFinally();
119 | return;
120 | }
121 |
122 | // No finally block was provided, thus we need to evaluate fulfillment
123 | // or rejection.
124 | // If the appropriate handler is not provided, it is forwarded to the
125 | // next promise
126 | if (that.state == STATE.FULFILLED) {
127 | evaluateFulfilled();
128 | return;
129 | }
130 |
131 | if (that.state == STATE.REJECTED) {
132 | evaluateRejected();
133 | return;
134 | }
135 | } catch (Throwable e) {
136 | // On any exception in the handlers above, we should throw the
137 | // exception to the next promise
138 |
139 | deferred.reject(e);
140 | }
141 | }
142 |
143 | private void evaluateFinally() {
144 | AbstractPromise> result = callFinally();
145 |
146 | if (result != null) {
147 | // the finally block returned a promise, so we need to delay
148 | // fulfillment of the next promise until the returned promise is
149 | // fulfilled
150 | ((AbstractPromise super Object>) result)._then(
151 | new Action1