├── .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) result)._then( 151 | new Action1() { 152 | @Override 153 | public void call(Object v) { 154 | deferred.fulfill((O) that.value); 155 | } 156 | }, new Action1() { 157 | @Override 158 | public void call(Throwable e) { 159 | deferred.reject(e); 160 | } 161 | }, null 162 | ); 163 | } else { 164 | // nothing was returned by the finally block. We can go ahead and 165 | // forward the value/reason held by this promise on to the next 166 | // one for resolution 167 | if (that.state == STATE.FULFILLED) { 168 | deferred.fulfill((O) that.value); 169 | } else { 170 | deferred.reject(that.reason); 171 | } 172 | } 173 | 174 | } 175 | 176 | private void evaluateFulfilled() { 177 | if (onFulfilled != null) { 178 | Object result = callFunction(onFulfilled, that.value); 179 | evalResult(result); 180 | } else { 181 | // Sends the value forward. We assume that the casting will pass 182 | deferred.fulfill((O) that.value); 183 | } 184 | } 185 | 186 | private void evaluateRejected() { 187 | if (onRejected != null) { 188 | // Allow this handler to recover from the rejection 189 | Object result = callFunction(onRejected, that.reason); 190 | evalResult(result); 191 | } else { 192 | // Forward it to the next promise 193 | deferred.reject(that.reason); 194 | } 195 | } 196 | 197 | private AbstractPromise callFinally() { 198 | if (onFinally instanceof Func0) { 199 | return (AbstractPromise) ((Func0) onFinally).call(); 200 | } 201 | 202 | ((Action0) onFinally).call(); 203 | return null; 204 | } 205 | 206 | private Object callFunction(Function function, O value) throws IllegalArgumentException { 207 | if (function instanceof Action0) { 208 | ((Action0) function).call(); 209 | return null; 210 | } 211 | 212 | if (function instanceof Action1) { 213 | ((Action1) function).call(value); 214 | return null; 215 | } 216 | 217 | if (function instanceof Func0) { 218 | return ((Func0) function).call(); 219 | } 220 | 221 | if (function instanceof Func1) { 222 | return ((Func1) function).call(value); 223 | } 224 | 225 | throw new IllegalArgumentException("Could not correctly invoke callback function with type " + function.getClass().toString()); 226 | } 227 | 228 | // takes a result and either converts it to a promise or sends it forward 229 | // for fulfillment 230 | @SuppressWarnings("unchecked") 231 | private void evalResult(Object result) { 232 | if (result instanceof AbstractPromise) { 233 | deferred.become((AbstractPromise) result); 234 | } else { 235 | deferred.fulfill((O) result); 236 | } 237 | } 238 | }; 239 | 240 | this.obs.subscribe(observer); 241 | 242 | return deferred; 243 | } 244 | 245 | /* Result Methods */ 246 | public void fulfill(T value) { 247 | this.subject.onNext(value); 248 | this.subject.onCompleted(); 249 | } 250 | 251 | public void reject(Object reason) { 252 | this.subject.onError(new Exception(reason.toString())); 253 | } 254 | 255 | public void reject(Throwable reason) { 256 | if (reason instanceof Exception) { 257 | this.subject.onError(reason); 258 | } else { 259 | this.subject.onError(OnErrorThrowable.from(reason)); 260 | } 261 | } 262 | 263 | public void become(AbstractPromise other) { 264 | other.subject.subscribe(this); 265 | } 266 | 267 | /* Observable Methods */ 268 | @Override 269 | public void onCompleted() { 270 | this.fulfill(this.value); 271 | } 272 | 273 | @Override 274 | public void onError(Throwable e) { 275 | this.reject(e); 276 | } 277 | 278 | @Override 279 | public void onNext(T value) { 280 | this.value = value; 281 | } 282 | 283 | /* rx adapter */ 284 | public Observable toObservable() { 285 | return this.obs; 286 | } 287 | 288 | /* Private Methods */ 289 | @SuppressWarnings("unchecked") 290 | private AbstractPromise _create() { 291 | try { 292 | return (AbstractPromise) this.getClass().newInstance(); 293 | } catch (Exception e) { 294 | throw new RuntimeException(e); 295 | } 296 | } 297 | 298 | } 299 | -------------------------------------------------------------------------------- /rxjava-promises/rxjava-promises-groovy/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'groovy' 2 | 3 | dependencies { 4 | compile "org.codehaus.groovy:groovy-all:2.2.1" 5 | } 6 | -------------------------------------------------------------------------------- /rxjava-promises/rxjava-promises-groovy/src/main/groovy/com/darylteo/rx/promises/groovy/Promise.groovy: -------------------------------------------------------------------------------- 1 | package com.darylteo.rx.promises.groovy 2 | 3 | import com.darylteo.rx.promises.AbstractPromise 4 | import rx.functions.Action0 5 | import rx.functions.Func1 6 | 7 | public class Promise extends AbstractPromise { 8 | public Promise() { 9 | super(); 10 | } 11 | 12 | public Promise(rx.Observable source) { 13 | super(source); 14 | } 15 | 16 | public Promise then(Map m = [:]) { 17 | return this.promise(m.onFulfilled, m.onRejected, null) 18 | } 19 | 20 | public Promise then(Closure onFulfilled, Closure onRejected = null) { 21 | return this.promise(onFulfilled, onRejected, null); 22 | } 23 | 24 | public Promise fail(Closure onRejected) { 25 | return this.promise(null, onRejected, null); 26 | } 27 | 28 | public Promise fin(Closure onFinally) { 29 | return this.promise(null, null, onFinally); 30 | } 31 | 32 | private Promise promise(Closure onFulfilled, Closure onRejected, Closure onFinally) { 33 | return (Promise) super._then(onFulfilled as Func1, onRejected as Func1, onFinally as Action0) 34 | } 35 | } -------------------------------------------------------------------------------- /rxjava-promises/rxjava-promises-groovy/src/test/groovy/com/darylteo/rx/promises/groovy/tests/PromisesTestGroovy.groovy: -------------------------------------------------------------------------------- 1 | package com.darylteo.rx.promises.groovy.tests 2 | 3 | import com.darylteo.rx.promises.groovy.Promise 4 | import org.junit.Test 5 | 6 | import java.util.concurrent.CountDownLatch 7 | import java.util.concurrent.TimeUnit 8 | 9 | import static org.junit.Assert.* 10 | 11 | /* http://promises-aplus.github.io/promises-spec/ */ 12 | 13 | class PromisesTestGroovy { 14 | @Test 15 | public void testStates() { 16 | def promise = new Promise() 17 | assertTrue(promise.pending) 18 | promise.fulfill('Hello') 19 | assertTrue(promise.fulfilled) 20 | assertEquals('Hello', promise.value) 21 | promise.reject(false) 22 | assertTrue(promise.fulfilled) 23 | assertNull(promise.reason) 24 | assertEquals('Hello', promise.value) 25 | promise.fulfill('World') 26 | assertTrue(promise.fulfilled) 27 | assertEquals('Hello', promise.value) 28 | 29 | promise = new Promise() 30 | assertTrue(promise.pending) 31 | promise.reject('Foo') 32 | assertTrue(promise.rejected) 33 | assertEquals('Foo', promise.reason?.message) 34 | promise.fulfill(true) 35 | assertTrue(promise.rejected) 36 | assertNull(promise.value) 37 | assertEquals('Foo', promise.reason?.message) 38 | promise.reject('Bar') 39 | assertTrue(promise.rejected) 40 | assertEquals('Foo', promise.reason?.message) 41 | } 42 | 43 | @Test 44 | public void testThenSyntax() { 45 | /* Both onFulfilled and onRejected are optional arguments */ 46 | makePromise('Hello').then { 47 | } 48 | makePromise('Hello').then(null, { 49 | }) 50 | makePromise('Hello').then({ 51 | }, { 52 | }) 53 | makePromise('Hello').then onFulfilled: { 54 | } 55 | makePromise('Hello').then onRejected: { 56 | } 57 | makePromise('Hello').then onFulfilled: { 58 | }, onRejected: { 59 | } 60 | makePromise('Hello').then() 61 | } 62 | 63 | @Test 64 | public void testThen() { 65 | CountDownLatch latch = new CountDownLatch(3) 66 | 67 | makePromise('Hello').then { result -> 68 | assertEquals 'Hello', result 69 | latch.countDown() 70 | return result.toUpperCase() 71 | }.then { 72 | assertEquals 'HELLO', it 73 | latch.countDown() 74 | }.then { makePromise('Foo') }.then { result -> 75 | assertEquals 'Foo', result 76 | latch.countDown() 77 | } 78 | 79 | latch.await(2l, TimeUnit.SECONDS); 80 | assertEquals 0, latch.count 81 | } 82 | 83 | @Test 84 | public void testReject() { 85 | CountDownLatch latch = new CountDownLatch(2) 86 | def invalids = [] 87 | 88 | makeRejection('Hello').then { result -> invalids += result } 89 | makeRejection('Hello').then({ result -> invalids += result }, { latch.countDown() }) 90 | makeRejection('Hello').then onFulfilled: { result -> invalids += result }, onRejected: { latch.countDown() } 91 | 92 | latch.await(2l, TimeUnit.SECONDS); 93 | assertTrue invalids.empty 94 | assertEquals 0, latch.count 95 | } 96 | 97 | @Test 98 | public void testFinally() { 99 | CountDownLatch latch = new CountDownLatch(5) 100 | def invalids = [] 101 | 102 | makeRejection('Hello').fin { latch.countDown() } 103 | makeRejection('Hello').fin { 104 | latch.countDown() 105 | return 'Foo' 106 | }.then({ result -> invalids += result }, { latch.countDown() }) 107 | makeRejection('Hello').fin { 108 | latch.countDown() 109 | makePromise('Foo') 110 | }.then({ result -> invalids += result }, { latch.countDown() }) 111 | makeRejection('Hello').then { result -> invalids += result }.fin { latch.countDown() }.fail { latch.countDown() } 112 | 113 | latch.await(2l, TimeUnit.SECONDS); 114 | assertTrue invalids.empty 115 | assertEquals 0, latch.count 116 | } 117 | 118 | 119 | private Promise makePromise(String message) { 120 | Promise p = new Promise() 121 | 122 | Thread.start { 123 | sleep 250 124 | p.fulfill message 125 | } 126 | 127 | return p 128 | } 129 | 130 | private Promise makeRejection(String message) { 131 | Promise p = new Promise() 132 | 133 | Thread.start { 134 | sleep 250 135 | p.reject 'Foobar' 136 | } 137 | 138 | return p 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /rxjava-promises/rxjava-promises-java/src/main/java/com/darylteo/rx/promises/java/Promise.java: -------------------------------------------------------------------------------- 1 | package com.darylteo.rx.promises.java; 2 | 3 | import com.darylteo.rx.promises.AbstractPromise; 4 | import com.darylteo.rx.promises.java.functions.*; 5 | import rx.Observable; 6 | import rx.functions.Function; 7 | import rx.subjects.ReplaySubject; 8 | 9 | /** 10 | * A Promise represents a request that will be fulfilled sometime in the future, most usually by an asynchrous task executed on the Vert.x Event Loop. It allows you to assign handlers to deal with the return results of asynchronus tasks, and to flatten "pyramids of doom" or "callback hell". 11 | *

12 | * Promise Rules 13 | *

    14 | *
  • A Promise represents a value that is set in some future time (usually in another cycle of the event loop)
  • 15 | *
  • Each promise has three components: onFulfilled, onRejected, and onFinally
  • 16 | *
  • If the promise is fulfilled, onFulfilled is called with the value of the promise.
  • 17 | *
  • If the promise cannot be fulfilled for some reason, it is then rejected. onRejected is called with the reason for the rejection
  • 18 | *
  • A promise may be further deferred, at which point a new promise is provided. This can lead to a chain of promises.
  • 19 | *
  • If a promise if fulfilled, but onFulfilled is not provided, then the promise is fulfilled with the same value.
  • 20 | *
  • If a promise is rejected, but onRejected is not provided, then the next promise is rejected with the same reason
  • 21 | *
  • If onFinally is provided, it is resolved first before either fulfilling or rejecting the next promise (see previous two points) 22 | *
  • Either onFulfilled, or onRejected may return a new promise (i.e. a repromise) . When this happens, the subsequently created promise will be fulfilled with the value of the repromise when it is eventually fulfilled.
  • 23 | *
24 | *

25 | * Type-Safe Rules 26 | *

    27 | *
  • All the type-safety rules are related to the output type of onFulfilled.
  • 28 | *
  • If onFulfilled returns type T, then onRejected must either return T, or null. This is facilitated through the use of PromiseFunction, and PromiseAction respectively.
  • 29 | *
  • As per the previous rule, if onFulfilled is an PromiseAction, then onRejected must also be an PromiseAction.
  • 30 | *
  • You may use a RepromiseFunction in place of a PromiseFunction that returns T.
  • 31 | *
  • If onFulfilled is not provided, it is assumed that it is defined in a future handler. As such, onRejected may not change the return type.
  • 32 | *
  • onFinally must be a Repromise or an Action. It does not accept any values. This is facilitated through the use of FinallyFunction and FinallyAction respectively.
  • 33 | *
34 | * 35 | * @param T - the data type of the result contained by this Promise. 36 | * @author Daryl Teo 37 | */ 38 | public class Promise extends AbstractPromise { 39 | public Promise() { 40 | } 41 | 42 | public Promise(Observable source) { 43 | super(source); 44 | } 45 | 46 | /* ================== */ 47 | /* Strictly Typed Defer Methods */ 48 | // then(onFulfilled) 49 | public Promise then(PromiseFunction onFulfilled) { 50 | return this.promise(onFulfilled, null, null); 51 | } 52 | 53 | public Promise then(RepromiseFunction onFulfilled) { 54 | return this.promise(onFulfilled, null, null); 55 | } 56 | 57 | public Promise then(PromiseAction onFulfilled) { 58 | return this.promise(onFulfilled, null, null); 59 | } 60 | 61 | // then(onFulfilled, onRejected) 62 | public Promise then(PromiseFunction onFulfilled, 63 | PromiseFunction onRejected) { 64 | return this.promise(onFulfilled, onRejected, null); 65 | } 66 | 67 | public Promise then(PromiseFunction onFulfilled, 68 | RepromiseFunction onRejected) { 69 | return this.promise(onFulfilled, onRejected, null); 70 | } 71 | 72 | public Promise then(PromiseFunction onFulfilled, 73 | PromiseAction onRejected) { 74 | return this.promise(onFulfilled, onRejected, null); 75 | } 76 | 77 | public Promise then(RepromiseFunction onFulfilled, 78 | PromiseFunction onRejected) { 79 | return this.promise(onFulfilled, onRejected, null); 80 | } 81 | 82 | public Promise then(RepromiseFunction onFulfilled, 83 | RepromiseFunction onRejected) { 84 | return this.promise(onFulfilled, onRejected, null); 85 | } 86 | 87 | public Promise then(RepromiseFunction onFulfilled, 88 | PromiseAction onRejected) { 89 | return this.promise(onFulfilled, onRejected, null); 90 | } 91 | 92 | public Promise then(PromiseAction onFulfilled, 93 | PromiseAction onRejected) { 94 | return this.promise(onFulfilled, onRejected, null); 95 | } 96 | 97 | // fail(onRejected) 98 | public Promise fail(PromiseFunction onRejected) { 99 | return this.promise(null, onRejected, null); 100 | } 101 | 102 | public Promise fail(RepromiseFunction onRejected) { 103 | return this.promise(null, onRejected, null); 104 | } 105 | 106 | public Promise fail(PromiseAction onRejected) { 107 | return this.promise(null, onRejected, null); 108 | } 109 | 110 | // fin(onFinally) 111 | public Promise fin(FinallyFunction onFinally) { 112 | return this.promise(null, null, onFinally); 113 | } 114 | 115 | public Promise fin(FinallyAction onFinally) { 116 | return this.promise(null, null, onFinally); 117 | } 118 | 119 | @SuppressWarnings("unchecked") 120 | protected Promise promise(Function onFulfilled, Function onRejected, Function onFinally) { 121 | return (Promise) super._then(onFulfilled, onRejected, onFinally); 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /rxjava-promises/rxjava-promises-java/src/main/java/com/darylteo/rx/promises/java/functions/FinallyAction.java: -------------------------------------------------------------------------------- 1 | package com.darylteo.rx.promises.java.functions; 2 | 3 | import rx.functions.Action0; 4 | 5 | public interface FinallyAction extends Action0 { 6 | } -------------------------------------------------------------------------------- /rxjava-promises/rxjava-promises-java/src/main/java/com/darylteo/rx/promises/java/functions/FinallyFunction.java: -------------------------------------------------------------------------------- 1 | package com.darylteo.rx.promises.java.functions; 2 | 3 | import com.darylteo.rx.promises.java.Promise; 4 | import rx.functions.Func0; 5 | 6 | public interface FinallyFunction extends Func0> { 7 | } -------------------------------------------------------------------------------- /rxjava-promises/rxjava-promises-java/src/main/java/com/darylteo/rx/promises/java/functions/PromiseAction.java: -------------------------------------------------------------------------------- 1 | package com.darylteo.rx.promises.java.functions; 2 | 3 | import rx.functions.Action1; 4 | 5 | public interface PromiseAction extends Action1 { 6 | } -------------------------------------------------------------------------------- /rxjava-promises/rxjava-promises-java/src/main/java/com/darylteo/rx/promises/java/functions/PromiseFunction.java: -------------------------------------------------------------------------------- 1 | package com.darylteo.rx.promises.java.functions; 2 | 3 | import rx.functions.Func1; 4 | 5 | public interface PromiseFunction extends Func1 { 6 | } -------------------------------------------------------------------------------- /rxjava-promises/rxjava-promises-java/src/main/java/com/darylteo/rx/promises/java/functions/RepromiseFunction.java: -------------------------------------------------------------------------------- 1 | package com.darylteo.rx.promises.java.functions; 2 | 3 | import com.darylteo.rx.promises.java.Promise; 4 | import rx.functions.Func1; 5 | 6 | public interface RepromiseFunction extends Func1> { 7 | } -------------------------------------------------------------------------------- /rxjava-promises/rxjava-promises-java/src/test/java/com/darylteo/rx/promises/test/PromiseRxJavaTests.java: -------------------------------------------------------------------------------- 1 | package com.darylteo.rx.promises.test; 2 | 3 | import com.darylteo.rx.promises.java.Promise; 4 | import com.darylteo.rx.promises.java.functions.PromiseAction; 5 | import org.junit.Test; 6 | import rx.Observable; 7 | import rx.functions.Action1; 8 | import rx.functions.Func1; 9 | 10 | import java.util.concurrent.CountDownLatch; 11 | import java.util.concurrent.TimeUnit; 12 | 13 | import static org.junit.Assert.assertEquals; 14 | 15 | public class PromiseRxJavaTests { 16 | 17 | @Test 18 | public void testPromiseToRxSubscribe() throws InterruptedException { 19 | final CountDownLatch latch = new CountDownLatch(1); 20 | final Result result = new Result(); 21 | 22 | makePromise("Hello World") 23 | .toObservable() 24 | .subscribe(new Action1() { 25 | @Override 26 | public void call(String value) { 27 | result.value = value; 28 | latch.countDown(); 29 | } 30 | }); 31 | 32 | latch.await(); 33 | assertEquals("Hello World", result.value); 34 | } 35 | 36 | @Test 37 | public void testPromiseToRxMap() throws InterruptedException { 38 | final CountDownLatch latch = new CountDownLatch(1); 39 | final Result result = new Result(); 40 | 41 | makePromise("Hello World") 42 | .toObservable() 43 | .map(new Func1() { 44 | @Override 45 | public String call(String value) { 46 | return value.toUpperCase(); 47 | } 48 | }) 49 | .subscribe(new Action1() { 50 | @Override 51 | public void call(String value) { 52 | result.value = value; 53 | latch.countDown(); 54 | } 55 | }); 56 | 57 | latch.await(); 58 | assertEquals("HELLO WORLD", result.value); 59 | } 60 | 61 | @Test 62 | public void testPromiseFromObservable() throws InterruptedException { 63 | final CountDownLatch latch = new CountDownLatch(1); 64 | final Result result = new Result(); 65 | 66 | Observable obs = Observable.from(new String[]{ 67 | "Hello", 68 | "World" 69 | }); 70 | 71 | new Promise(obs) 72 | .then(new PromiseAction() { 73 | @Override 74 | public void call(String message) { 75 | result.value = "World"; 76 | latch.countDown(); 77 | } 78 | }); 79 | 80 | latch.await(2l, TimeUnit.SECONDS); 81 | assertEquals("World", result.value); 82 | } 83 | 84 | public Promise makePromise(final String value) { 85 | final Promise promise = new Promise(); 86 | 87 | new Thread() { 88 | public void run() { 89 | try { 90 | Thread.sleep(500); 91 | } catch (InterruptedException e) { 92 | // TODO Auto-generated catch block 93 | e.printStackTrace(); 94 | } finally { 95 | promise.fulfill(value); 96 | } 97 | } 98 | }.start(); 99 | 100 | return promise; 101 | } 102 | 103 | private class Result { 104 | T value; 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /rxjava-promises/rxjava-promises-java/src/test/java/com/darylteo/rx/promises/test/PromiseTestsJava.java: -------------------------------------------------------------------------------- 1 | package com.darylteo.rx.promises.test; 2 | 3 | import com.darylteo.rx.promises.java.Promise; 4 | import com.darylteo.rx.promises.java.functions.*; 5 | import org.junit.Test; 6 | import rx.exceptions.OnErrorThrowable; 7 | 8 | import java.util.concurrent.CountDownLatch; 9 | import java.util.concurrent.TimeUnit; 10 | import java.util.concurrent.atomic.AtomicBoolean; 11 | 12 | import static org.junit.Assert.*; 13 | 14 | public class PromiseTestsJava { 15 | 16 | @Test 17 | public void testDefer() throws Exception { 18 | Promise promise = new Promise(); 19 | 20 | assertNotNull(promise); 21 | assertTrue(promise instanceof Promise); 22 | } 23 | 24 | @Test 25 | public void testDefer2() throws Exception { 26 | Promise promise = makePromise("Hello World"); 27 | 28 | assertNotNull(promise != null); 29 | assertNotNull(promise instanceof Promise); 30 | } 31 | 32 | /* Basic handler */ 33 | @Test 34 | public void testBasic() throws Exception { 35 | final CountDownLatch latch = new CountDownLatch(1); 36 | final Result result = new Result(); 37 | 38 | makePromise("Hello World") 39 | .then(new PromiseAction() { 40 | @Override 41 | public void call(String message) { 42 | result.value = message; 43 | latch.countDown(); 44 | } 45 | }); 46 | 47 | latch.await(2l, TimeUnit.SECONDS); 48 | assertEquals("Hello World", result.value); 49 | } 50 | 51 | /* Test of Handlers - return Value */ 52 | @Test 53 | public void testChain1() throws Exception { 54 | final CountDownLatch latch = new CountDownLatch(1); 55 | final Result result = new Result(); 56 | 57 | makePromise("Hello World") 58 | .then(new PromiseFunction() { 59 | @Override 60 | public String call(String result) { 61 | return result.toUpperCase(); 62 | } 63 | }) 64 | .then(new PromiseAction() { 65 | @Override 66 | public void call(String message) { 67 | result.value = message; 68 | latch.countDown(); 69 | } 70 | }); 71 | latch.await(2l, TimeUnit.SECONDS); 72 | assertEquals("HELLO WORLD", result.value); 73 | } 74 | 75 | /* Chain of handlers - return Promise */ 76 | @Test 77 | public void testChain2() throws Exception { 78 | final CountDownLatch latch = new CountDownLatch(1); 79 | final Result result = new Result(); 80 | 81 | makePromise("Hello World") 82 | .then(new RepromiseFunction() { 83 | @Override 84 | public Promise call(final String result) { 85 | return makePromise(result.toUpperCase()); 86 | } 87 | }) 88 | .then(new PromiseAction() { 89 | @Override 90 | public void call(String message) { 91 | result.value = message; 92 | latch.countDown(); 93 | } 94 | }); 95 | latch.await(2l, TimeUnit.SECONDS); 96 | assertEquals("HELLO WORLD", result.value); 97 | } 98 | 99 | /* Chain of handlers - forwarding on */ 100 | @Test 101 | public void testChain3() throws Exception { 102 | final CountDownLatch latch = new CountDownLatch(1); 103 | final Result result = new Result(); 104 | 105 | makePromise("Hello World") 106 | .then(new RepromiseFunction() { 107 | @Override 108 | public Promise call(final String result) { 109 | return makePromise(result.toUpperCase()); 110 | } 111 | }) 112 | .fail(new PromiseFunction() { 113 | @Override 114 | public String call(Exception e) { 115 | fail("This should not occur"); 116 | return "fail"; 117 | } 118 | }) 119 | .then(new PromiseAction() { 120 | @Override 121 | public void call(String message) { 122 | result.value = message; 123 | latch.countDown(); 124 | } 125 | }); 126 | 127 | latch.await(2l, TimeUnit.SECONDS); 128 | assertEquals("HELLO WORLD", result.value); 129 | } 130 | 131 | @Test 132 | public void testMultiple1() throws Exception { 133 | final CountDownLatch latch = new CountDownLatch(2); 134 | 135 | Promise mainPromise = makePromise("Hello World"); 136 | 137 | mainPromise.then(new PromiseAction() { 138 | @Override 139 | public void call(String result) { 140 | System.out.println("Before"); 141 | assertEquals(latch.getCount(), 2); 142 | latch.countDown(); 143 | } 144 | }); 145 | mainPromise.then(new PromiseAction() { 146 | @Override 147 | public void call(String result) { 148 | System.out.println("After"); 149 | assertEquals(latch.getCount(), 1); 150 | latch.countDown(); 151 | } 152 | }); 153 | 154 | latch.await(2l, TimeUnit.SECONDS); 155 | } 156 | 157 | /* Exception with then() handler */ 158 | @Test 159 | public void testException1() throws Exception { 160 | final CountDownLatch latch = new CountDownLatch(1); 161 | 162 | makePromise("Hello World") 163 | .then(new PromiseFunction() { 164 | @Override 165 | public Character call(String result) { 166 | return result.charAt(20); // Exception 167 | } 168 | }).then( 169 | new PromiseAction() { 170 | @Override 171 | public void call(Character value) { 172 | fail("Promise not correctly calling failure handler when exception or rejection occurs"); 173 | } 174 | }, 175 | new PromiseAction() { 176 | @Override 177 | public void call(Exception e) { 178 | assertEquals("Exception is not StringIndexOutOfBoundsException", StringIndexOutOfBoundsException.class, e.getClass()); 179 | latch.countDown(); 180 | } 181 | } 182 | ); 183 | 184 | latch.await(2l, TimeUnit.SECONDS); 185 | } 186 | 187 | /* Exception with fail() handler */ 188 | @Test 189 | public void testException2() throws Exception { 190 | final CountDownLatch latch = new CountDownLatch(1); 191 | 192 | makePromise("Hello World") 193 | .then(new PromiseFunction() { 194 | @Override 195 | public Character call(String result) { 196 | return result.charAt(20); // Exception 197 | } 198 | }).fail( 199 | new PromiseAction() { 200 | @Override 201 | public void call(Exception e) { 202 | assertEquals("Exception is not StringIndexOutOfBoundsException", StringIndexOutOfBoundsException.class, e.getClass()); 203 | latch.countDown(); 204 | } 205 | } 206 | ); 207 | 208 | latch.await(2l, TimeUnit.SECONDS); 209 | } 210 | 211 | /* Exception with fail() handler */ 212 | @Test 213 | public void testException3() throws Exception { 214 | final CountDownLatch latch = new CountDownLatch(1); 215 | 216 | makePromise("Hello World") 217 | .then( 218 | new PromiseFunction() { 219 | @Override 220 | public Character call(String result) { 221 | return result.charAt(20); // Exception 222 | } 223 | }, 224 | new PromiseAction() { 225 | @Override 226 | public void call(Exception e) { 227 | fail("This rejection handler should not be called!"); 228 | } 229 | } 230 | ).fail( 231 | new PromiseAction() { 232 | @Override 233 | public void call(Exception e) { 234 | assertTrue(e instanceof StringIndexOutOfBoundsException); 235 | latch.countDown(); 236 | } 237 | } 238 | ); 239 | 240 | latch.await(2l, TimeUnit.SECONDS); 241 | } 242 | 243 | /* Exception with handler */ 244 | @Test 245 | public void testException4() throws Exception { 246 | final CountDownLatch latch = new CountDownLatch(1); 247 | final AtomicBoolean flag = new AtomicBoolean(false); 248 | 249 | makePromise("Hello World") 250 | .then(new PromiseFunction() { 251 | @Override 252 | public Character call(String result) { 253 | return result.charAt(20); // Exception 254 | } 255 | }).then( 256 | new PromiseFunction() { 257 | @Override 258 | public String call(Character value) { 259 | fail("Promise not correctly calling failure handler when exception or rejection occurs"); 260 | return "The Char is : " + value; 261 | } 262 | }, 263 | new PromiseFunction() { 264 | @Override 265 | public String call(Exception value) { 266 | flag.set(true); 267 | return null; 268 | } 269 | } 270 | ).then(new PromiseAction() { 271 | @Override 272 | public void call(String value) { 273 | assertNull(value); 274 | assertTrue("FailureHandler was not called", flag.get()); 275 | latch.countDown(); 276 | } 277 | }); 278 | 279 | latch.await(2l, TimeUnit.SECONDS); 280 | } 281 | 282 | @Test 283 | public void testException5() throws Exception { 284 | final CountDownLatch latch = new CountDownLatch(1); 285 | 286 | makePromise("Hello World") 287 | .then(new PromiseFunction() { 288 | @Override 289 | public Character call(String result) { 290 | return result.charAt(20); // Exception 291 | } 292 | }) 293 | .then(new PromiseAction() { 294 | @Override 295 | public void call(Character value) { 296 | latch.countDown(); 297 | } 298 | }); 299 | 300 | Thread.sleep(2000); 301 | assertEquals("Promise did not fail property", latch.getCount(), 1); 302 | } 303 | 304 | /* Test exception passing to further promises */ 305 | @Test 306 | public void testException7() throws Exception { 307 | final CountDownLatch latch = new CountDownLatch(1); 308 | final AtomicBoolean flag = new AtomicBoolean(false); 309 | 310 | makePromise("Hello World") 311 | .then(new PromiseFunction() { 312 | @Override 313 | public Character call(String result) { 314 | return result.charAt(20); // Exception 315 | } 316 | }) 317 | .then(new PromiseAction() { 318 | @Override 319 | public void call(Character value) { 320 | flag.set(true); 321 | } 322 | }) 323 | .fail(new PromiseAction() { 324 | @Override 325 | public void call(Exception e) { 326 | latch.countDown(); 327 | } 328 | }); 329 | 330 | latch.await(2l, TimeUnit.SECONDS); 331 | assertFalse("Promise did not fail properly", flag.get()); 332 | } 333 | 334 | @Test 335 | public void testError() throws Exception { 336 | final Promise p = new Promise(); 337 | final CountDownLatch latch = new CountDownLatch(1); 338 | final Result result = new Result(); 339 | 340 | p.then(new PromiseAction() { 341 | @Override 342 | public void call(String s) { 343 | throw new AssertionError("Hello World"); 344 | } 345 | }).fail(new PromiseAction() { 346 | @Override 347 | public void call(Exception e) { 348 | result.value = e; 349 | latch.countDown(); 350 | } 351 | }); 352 | 353 | p.fulfill("Go"); 354 | latch.await(1, TimeUnit.SECONDS); 355 | assertNotNull(result.value); 356 | assertEquals(result.value.getClass(), OnErrorThrowable.class); 357 | assertEquals(result.value.getCause().getClass(), AssertionError.class); 358 | } 359 | 360 | /* Fin with basic */ 361 | @Test 362 | public void testFinally1() throws Exception { 363 | final CountDownLatch latch = new CountDownLatch(1); 364 | 365 | makePromise("Hello World") 366 | .then(new PromiseFunction() { 367 | @Override 368 | public Character call(String result) { 369 | return result.charAt(0); 370 | } 371 | }) 372 | .fin(new FinallyAction() { 373 | @Override 374 | public void call() { 375 | latch.countDown(); 376 | } 377 | }); 378 | 379 | latch.await(2l, TimeUnit.SECONDS); 380 | } 381 | 382 | /* Fin with Exception */ 383 | @Test 384 | public void testFinally2() throws Exception { 385 | final CountDownLatch latch = new CountDownLatch(1); 386 | 387 | makePromise("Hello World") 388 | .then(new PromiseFunction() { 389 | @Override 390 | public Character call(String result) { 391 | return result.charAt(20); 392 | } 393 | }) 394 | .fin(new FinallyAction() { 395 | @Override 396 | public void call() { 397 | latch.countDown(); 398 | } 399 | }); 400 | 401 | latch.await(2l, TimeUnit.SECONDS); 402 | } 403 | 404 | /* then() handler after fin() */ 405 | @Test 406 | public void testFinally3() throws Exception { 407 | final CountDownLatch latch = new CountDownLatch(1); 408 | final Result result = new Result(); 409 | final AtomicBoolean flag = new AtomicBoolean(false); 410 | 411 | makePromise("Hello World") 412 | .then(new PromiseFunction() { 413 | @Override 414 | public Character call(String result) { 415 | return result.charAt(0); 416 | } 417 | }) 418 | .fin(new FinallyAction() { 419 | @Override 420 | public void call() { 421 | flag.set(true); 422 | } 423 | }) 424 | .then(new PromiseAction() { 425 | @Override 426 | public void call(Character value) { 427 | result.value = value; 428 | latch.countDown(); 429 | } 430 | }); 431 | 432 | latch.await(2l, TimeUnit.SECONDS); 433 | // value from promise must pass through 434 | // finally handler must fire 435 | // finally return value must be ignored 436 | assertEquals("Wrong value passed after calling finally", result.value, new Character('H')); 437 | assertTrue(flag.get()); 438 | } 439 | 440 | /* then() handler after fin() with promise(void) */ 441 | @Test 442 | public void testFinally4() throws Exception { 443 | final CountDownLatch latch = new CountDownLatch(1); 444 | final Result result = new Result(); 445 | final AtomicBoolean flag = new AtomicBoolean(false); 446 | 447 | makePromise("Hello World") 448 | .then(new PromiseFunction() { 449 | @Override 450 | public Character call(String result) { 451 | return result.charAt(0); 452 | } 453 | }) 454 | .fin(new FinallyFunction() { 455 | @Override 456 | public Promise call() { 457 | final Promise promise = new Promise(); 458 | 459 | makePromise("Foo Bar").then(new PromiseAction() { 460 | @Override 461 | public void call(String t1) { 462 | flag.set(true); 463 | promise.fulfill(null); 464 | } 465 | }); 466 | 467 | return promise; 468 | } 469 | }) 470 | .then(new PromiseAction() { 471 | @Override 472 | public void call(Character value) { 473 | result.value = value; 474 | latch.countDown(); 475 | } 476 | }); 477 | 478 | latch.await(5l, TimeUnit.SECONDS); 479 | 480 | // value from promise must pass through 481 | // finally handler must fire 482 | // finally return value must be ignored 483 | // then must fire only after finally has fulfilled promise 484 | assertEquals("Wrong value passed after calling finally", new Character('H'), result.value); 485 | assertTrue(flag.get()); 486 | } 487 | 488 | /* then() handler after fin() with promise(string) */ 489 | @Test 490 | public void testFinally5() throws Exception { 491 | final CountDownLatch latch = new CountDownLatch(1); 492 | final Result result = new Result(); 493 | final AtomicBoolean flag = new AtomicBoolean(false); 494 | 495 | makePromise("Hello World") 496 | .then(new PromiseFunction() { 497 | @Override 498 | public Character call(String result) { 499 | return result.charAt(0); 500 | } 501 | }) 502 | .fin(new FinallyFunction() { 503 | @Override 504 | public Promise call() { 505 | final Promise promise = new Promise(); 506 | 507 | makePromise("Foo Bar").then(new PromiseAction() { 508 | @Override 509 | public void call(String t1) { 510 | flag.set(true); 511 | promise.fulfill(t1); 512 | } 513 | }); 514 | 515 | return promise; 516 | } 517 | }) 518 | .then(new PromiseAction() { 519 | @Override 520 | public void call(Character value) { 521 | result.value = value; 522 | latch.countDown(); 523 | } 524 | }); 525 | 526 | latch.await(5l, TimeUnit.SECONDS); 527 | 528 | // make sure that the value returned from Finally is NOT passed through 529 | assertEquals("Wrong value passed after calling finally", new Character('H'), result.value); 530 | assertTrue(flag.get()); 531 | } 532 | 533 | /* then() rejection after fin() */ 534 | @Test 535 | public void testFinally6() throws Exception { 536 | final CountDownLatch latch = new CountDownLatch(1); 537 | final Result result = new Result(); 538 | final AtomicBoolean flag = new AtomicBoolean(false); 539 | 540 | makePromise("Hello World") 541 | .then(new PromiseFunction() { 542 | @Override 543 | public Character call(String result) { 544 | return result.charAt(20); 545 | } 546 | }) 547 | .fin(new FinallyAction() { 548 | @Override 549 | public void call() { 550 | flag.set(true); 551 | } 552 | }) 553 | .fail(new PromiseAction() { 554 | @Override 555 | public void call(Exception reason) { 556 | System.out.println(reason); 557 | result.value = reason; 558 | latch.countDown(); 559 | } 560 | }); 561 | 562 | latch.await(2l, TimeUnit.SECONDS); 563 | System.out.println(result.value instanceof StringIndexOutOfBoundsException); 564 | assertEquals("Exception was not StringIndexOutOfBoundsException", StringIndexOutOfBoundsException.class, result.value.getClass()); 565 | assertTrue(flag.get()); 566 | } 567 | 568 | @Test 569 | public void testPrefilled() throws Exception { 570 | Promise p = new Promise(); 571 | 572 | p.fulfill("Hello World"); 573 | 574 | p.then(new PromiseAction() { 575 | @Override 576 | public void call(String value) { 577 | assertEquals(value, "Hello World"); 578 | } 579 | }); 580 | } 581 | 582 | public Promise makePromise(final String value) { 583 | final Promise promise = new Promise(); 584 | 585 | new Thread() { 586 | public void run() { 587 | try { 588 | Thread.sleep(500); 589 | } catch (InterruptedException e) { 590 | // TODO Auto-generated catch block 591 | e.printStackTrace(); 592 | } finally { 593 | promise.fulfill(value); 594 | } 595 | } 596 | }.start(); 597 | 598 | return promise; 599 | } 600 | 601 | private class Result { 602 | T value; 603 | } 604 | } -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':rxjava-promises:rxjava-promises-core' 2 | 3 | include ':rxjava-promises:rxjava-promises-java' 4 | include ':rxjava-promises:rxjava-promises-groovy' 5 | 6 | include ':vertx-promises:vertx-promises-java' 7 | include ':vertx-promises:vertx-promises-groovy' 8 | -------------------------------------------------------------------------------- /vertx-promises/README.md: -------------------------------------------------------------------------------- 1 | # Vertx-Promises 2 | 3 | ### Maven 4 | ```XML 5 | 6 | com.darylteo.vertx 7 | vertx-promises-{lang} 8 | 1.2.0 9 | 10 | ```` 11 | ### mod.json 12 | ```javascript 13 | { 14 | includes: "com.darylteo.vertx~vertx-promises-{lang}~1.2.0" 15 | } 16 | ```` 17 | 18 | 19 | ## Vertx Specific Functionality 20 | 21 | The main addition to the Vertx version of RxJava-Promises is its ability to be used as a Vertx Handler. 22 | 23 | ```java 24 | private Promise readAFile(String filename) { 25 | final Promise p = new Promise(); 26 | 27 | vertx.fileSystem().readFile(filname, new Handler>() { 28 | @Override 29 | public void handle(AsyncResult event) { 30 | if (event.succeeded()) { 31 | p.fulfill(event.result()); 32 | } else { 33 | p.reject(event.cause()); 34 | } 35 | } 36 | }); 37 | 38 | return p; 39 | } 40 | ```` 41 | 42 | In the future there will be a more convenient way of coping with AsyncResult's succeed() value. 43 | -------------------------------------------------------------------------------- /vertx-promises/build.gradle: -------------------------------------------------------------------------------- 1 | subprojects { 2 | apply plugin: 'vertx' 3 | apply from: rootProject.file('gradle/sonatype.gradle') 4 | 5 | group = 'com.darylteo.vertx' 6 | version = '1.2.1' 7 | 8 | vertx { 9 | platform { 10 | version '2.0.2-final' 11 | tools '2.0.0-final' 12 | } 13 | 14 | config { 15 | } 16 | 17 | info { 18 | name "RxJava Promises - Vertx Module - ${project.name}" 19 | description "Promises module for Vert.x - ${project.name}" 20 | inceptionYear '2013' 21 | url 'http://github.com/darylteo/rxjava-promises' 22 | 23 | licenses { 24 | license { 25 | name 'The Apache Software License, Version 2.0' 26 | url 'http://www.apache.org/licenses/LICENSE-2.0.txt' 27 | distribution 'repo' 28 | } 29 | } 30 | 31 | developers { 32 | developer { 33 | id 'darylteo' 34 | name 'Daryl Teo' 35 | email 'i.am@darylteo.com' 36 | } 37 | } 38 | 39 | scm { url 'http://github.com/darylteo/rxjava-promises' } 40 | 41 | properties { 42 | keywords 'promise,promises,handler,future,continuation,callback,async' 43 | 'project.build.sourceEncoding' 'UTF8' 44 | } 45 | } 46 | } 47 | 48 | artifacts { archives modZip } 49 | 50 | [ 51 | uploadArchives.repositories.mavenDeployer.pom, 52 | install.repositories.mavenInstaller.pom 53 | ]*.withXml { 54 | asNode().children().addAll vertx.info 55 | } 56 | } -------------------------------------------------------------------------------- /vertx-promises/vertx-promises-groovy/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'groovy' 2 | 3 | vertx { 4 | platform { 5 | version '2.1RC3' 6 | lang 'groovy' 7 | } 8 | } 9 | 10 | dependencies { 11 | compile project(':rxjava-promises:rxjava-promises-core') 12 | provided 'org.codehaus.groovy:groovy-all:2.2.1' 13 | } 14 | -------------------------------------------------------------------------------- /vertx-promises/vertx-promises-groovy/src/main/groovy/com/darylteo/vertx/promises/groovy/Promise.groovy: -------------------------------------------------------------------------------- 1 | package com.darylteo.vertx.promises.groovy 2 | 3 | import com.darylteo.rx.promises.AbstractPromise 4 | import org.vertx.java.core.Handler 5 | import rx.functions.Action0 6 | import rx.functions.Func1 7 | 8 | public class Promise extends AbstractPromise implements Handler { 9 | public Promise() { 10 | super(); 11 | } 12 | 13 | public Promise(rx.Observable source) { 14 | super(source); 15 | } 16 | 17 | 18 | public Promise then(Map m = [:]) { 19 | return this.promise(m.onFulfilled, m.onRejected, null) 20 | } 21 | 22 | public Promise then(Closure onFulfilled, Closure onRejected = null) { 23 | return this.promise(onFulfilled, onRejected, null); 24 | } 25 | 26 | public Promise fail(Closure onRejected) { 27 | return this.promise(null, onRejected, null); 28 | } 29 | 30 | public Promise fin(Closure onFinally) { 31 | return this.promise(null, null, onFinally); 32 | } 33 | 34 | private Promise promise(Closure onFulfilled, Closure onRejected, Closure onFinally) { 35 | return (Promise) super._then(onFulfilled as Func1, onRejected as Func1, onFinally as Action0) 36 | } 37 | 38 | @Override 39 | public void handle(T event) { 40 | this.fulfill(event); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /vertx-promises/vertx-promises-java/.cache: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darylteo/rxjava-promises/c528e8e571b88b54653ab9eb39f9626e89ba9de9/vertx-promises/vertx-promises-java/.cache -------------------------------------------------------------------------------- /vertx-promises/vertx-promises-java/build.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | compile project(':rxjava-promises:rxjava-promises-core') 3 | } -------------------------------------------------------------------------------- /vertx-promises/vertx-promises-java/src/main/java/com/darylteo/vertx/promises/java/Promise.java: -------------------------------------------------------------------------------- 1 | package com.darylteo.vertx.promises.java; 2 | 3 | import com.darylteo.rx.promises.AbstractPromise; 4 | import com.darylteo.vertx.promises.java.functions.*; 5 | import org.vertx.java.core.Handler; 6 | import rx.Observable; 7 | import rx.functions.Function; 8 | 9 | public class Promise extends AbstractPromise implements Handler { 10 | public Promise() { 11 | super(); 12 | } 13 | 14 | public Promise(Observable source) { 15 | super(source); 16 | } 17 | 18 | /* ================== */ 19 | /* Strictly Typed Defer Methods */ 20 | // then(onFulfilled) 21 | public Promise then(PromiseFunction onFulfilled) { 22 | return this.promise(onFulfilled, null, null); 23 | } 24 | 25 | public Promise then(RepromiseFunction onFulfilled) { 26 | return this.promise(onFulfilled, null, null); 27 | } 28 | 29 | public Promise then(PromiseAction onFulfilled) { 30 | return this.promise(onFulfilled, null, null); 31 | } 32 | 33 | // then(onFulfilled, onRejected) 34 | public Promise then(PromiseFunction onFulfilled, 35 | PromiseFunction onRejected) { 36 | return this.promise(onFulfilled, onRejected, null); 37 | } 38 | 39 | public Promise then(PromiseFunction onFulfilled, 40 | RepromiseFunction onRejected) { 41 | return this.promise(onFulfilled, onRejected, null); 42 | } 43 | 44 | public Promise then(PromiseFunction onFulfilled, 45 | PromiseAction onRejected) { 46 | return this.promise(onFulfilled, onRejected, null); 47 | } 48 | 49 | public Promise then(RepromiseFunction onFulfilled, 50 | PromiseFunction onRejected) { 51 | return this.promise(onFulfilled, onRejected, null); 52 | } 53 | 54 | public Promise then(RepromiseFunction onFulfilled, 55 | RepromiseFunction onRejected) { 56 | return this.promise(onFulfilled, onRejected, null); 57 | } 58 | 59 | public Promise then(RepromiseFunction onFulfilled, 60 | PromiseAction onRejected) { 61 | return this.promise(onFulfilled, onRejected, null); 62 | } 63 | 64 | public Promise then(PromiseAction onFulfilled, 65 | PromiseAction onRejected) { 66 | return this.promise(onFulfilled, onRejected, null); 67 | } 68 | 69 | // fail(onRejected) 70 | public Promise fail(PromiseFunction onRejected) { 71 | return this.promise(null, onRejected, null); 72 | } 73 | 74 | public Promise fail(RepromiseFunction onRejected) { 75 | return this.promise(null, onRejected, null); 76 | } 77 | 78 | public Promise fail(PromiseAction onRejected) { 79 | return this.promise(null, onRejected, null); 80 | } 81 | 82 | // fin(onFinally) 83 | public Promise fin(FinallyFunction onFinally) { 84 | return this.promise(null, null, onFinally); 85 | } 86 | 87 | public Promise fin(FinallyAction onFinally) { 88 | return this.promise(null, null, onFinally); 89 | } 90 | 91 | @SuppressWarnings("unchecked") 92 | protected Promise promise(Function onFulfilled, Function onRejected, Function onFinally) { 93 | return (Promise) super._then(onFulfilled, onRejected, onFinally); 94 | } 95 | 96 | @Override 97 | public void handle(T event) { 98 | this.fulfill(event); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /vertx-promises/vertx-promises-java/src/main/java/com/darylteo/vertx/promises/java/functions/FinallyAction.java: -------------------------------------------------------------------------------- 1 | package com.darylteo.vertx.promises.java.functions; 2 | 3 | import rx.functions.Action0; 4 | 5 | public interface FinallyAction extends Action0 { 6 | } -------------------------------------------------------------------------------- /vertx-promises/vertx-promises-java/src/main/java/com/darylteo/vertx/promises/java/functions/FinallyFunction.java: -------------------------------------------------------------------------------- 1 | package com.darylteo.vertx.promises.java.functions; 2 | 3 | import com.darylteo.vertx.promises.java.Promise; 4 | import rx.functions.Func0; 5 | 6 | public interface FinallyFunction extends Func0> { 7 | } -------------------------------------------------------------------------------- /vertx-promises/vertx-promises-java/src/main/java/com/darylteo/vertx/promises/java/functions/PromiseAction.java: -------------------------------------------------------------------------------- 1 | package com.darylteo.vertx.promises.java.functions; 2 | 3 | import rx.functions.Action1; 4 | 5 | public interface PromiseAction extends Action1 { 6 | } -------------------------------------------------------------------------------- /vertx-promises/vertx-promises-java/src/main/java/com/darylteo/vertx/promises/java/functions/PromiseFunction.java: -------------------------------------------------------------------------------- 1 | package com.darylteo.vertx.promises.java.functions; 2 | 3 | import rx.functions.Func1; 4 | 5 | public interface PromiseFunction extends Func1 { 6 | } -------------------------------------------------------------------------------- /vertx-promises/vertx-promises-java/src/main/java/com/darylteo/vertx/promises/java/functions/RepromiseFunction.java: -------------------------------------------------------------------------------- 1 | package com.darylteo.vertx.promises.java.functions; 2 | 3 | import com.darylteo.vertx.promises.java.Promise; 4 | import rx.functions.Func1; 5 | 6 | public interface RepromiseFunction extends Func1> { 7 | } -------------------------------------------------------------------------------- /vertx-promises/vertx-promises-java/src/test/java/com/darylteo/vertx/promises/java/test/PromiseTests.java: -------------------------------------------------------------------------------- 1 | package com.darylteo.vertx.promises.java.test; 2 | 3 | import com.darylteo.vertx.promises.java.Promise; 4 | import com.darylteo.vertx.promises.java.functions.PromiseAction; 5 | import com.darylteo.vertx.promises.java.functions.PromiseFunction; 6 | import com.darylteo.vertx.promises.java.functions.RepromiseFunction; 7 | import org.junit.Test; 8 | import org.vertx.java.core.Handler; 9 | import org.vertx.testtools.TestVerticle; 10 | 11 | import static org.vertx.testtools.VertxAssert.assertEquals; 12 | import static org.vertx.testtools.VertxAssert.testComplete; 13 | 14 | public class PromiseTests extends TestVerticle { 15 | @Test 16 | public void testHandler() { 17 | Promise promise = new Promise(); 18 | vertx.setTimer(1000l, promise); 19 | 20 | promise.then(new PromiseFunction() { 21 | @Override 22 | public String call(Long timerID) { 23 | return "Hello World!"; 24 | } 25 | }).then(new RepromiseFunction() { 26 | @Override 27 | public Promise call(final String t1) { 28 | final Promise p = new Promise(); 29 | vertx.setTimer(1000l, new Handler() { 30 | @Override 31 | public void handle(Long event) { 32 | p.fulfill(t1.toUpperCase()); 33 | } 34 | }); 35 | 36 | return p; 37 | } 38 | }).then(new PromiseAction() { 39 | @Override 40 | public void call(String t1) { 41 | assertEquals(t1, "HELLO WORLD!"); 42 | testComplete(); 43 | } 44 | 45 | }); 46 | } 47 | } --------------------------------------------------------------------------------