diff --git a/eg/json-server-async/README.md b/eg/json-server-async/README.md
new file mode 100644
index 0000000..9ef495c
--- /dev/null
+++ b/eg/json-server-async/README.md
@@ -0,0 +1,77 @@
+# replyAsync Example
+
+This example demonstrates using replyAsync function. You should use
+**replyAsync** instead of **reply** if you have subroutines that return
+promises.
+
+## Important Note About Conditionals
+
+Asynchronous object macros are *not* supported within the conditional side of
+`*Conditions` in RiveScript. That is, RiveScript code like the following will
+not work (where the `weather` macro calls out to an HTTP API and returns a
+promise with the result):
+
+```rivescript
++ is it sunny outside
+* weather == sunny => It appears it is!
+- It doesn't look sunny outside.
+```
+
+Conditionals in RiveScript require their results to be immediately available
+so it can check if the comparison is truthy. So, even if you use `replyAsync()`,
+the `` tags in conditionals only support running synchronous object
+macros (ones that return a string result and not a promise).
+
+However, asynchronous object macros can be used in the *reply* portion of the
+conditional (the part on the right side of the `=>` separator). This is the
+text eventually returned to the user and it can return as a promise when you use
+`replyAsync()` just like if it were in a `-Reply` command.
+
+## Running the example
+
+To fully experience this example, you'll need to get an API key from
+[OpenWeatherMap](http://openweathermap.org/appid) (don't worry, they're free!),
+and edit `weatherman.js` to fill in your `APPID` near the top of the file.
+
+```bash
+npm install && node weatherman.js
+```
+
+Refer to weatherman.rive for the list of supported commands
+
+## Using async subroutines
+
+Whenever you have a subroutine that needs to call some sort of asynchronous
+function in order to return a value back to the script, you should use promises:
+
+```javascript
+var rs = new RiveScript();
+rs.setSubroutine("asyncHelper", function(rs, args) {
+ // RiveScript comes bundled with RSVP.js which you can
+ // access through RiveScript.Promise alias
+ // but you are free to use your own Promise implementation
+ return new rs.Promise(function(resolve, reject) {
+ resolve('hello there');
+ });
+})
+```
+
+Async responses in RiveScript come in 2 flavors:
+
+```javascript
+// using promises
+rs.replyAsync(username, message).then(function(reply) {
+ // good to go
+}).catch(function(error){
+ // something went wrong
+});
+
+// or using callbacks
+rs.replyAsync(username, message, this, function(error, reply) {
+ if (!error) {
+ // you can use reply here
+ } else {
+ // something went wrong, error has more info
+ }
+});
+```
diff --git a/eg/json-server-async/app.js b/eg/json-server-async/app.js
new file mode 100644
index 0000000..192c4b1
--- /dev/null
+++ b/eg/json-server-async/app.js
@@ -0,0 +1,227 @@
+// Asynchronous Objects Example
+// See the accompanying README.md for details.
+
+// Run this demo: `node weatherman.js`
+
+var readline = require("readline");
+var request = require("request");
+var colors = require('colors');
+
+// Configuration: get an API key from http://openweathermap.org/appid and
+// put it in this variable.
+const APPID = 'CHAMGEME';
+
+// This would just be require("rivescript") if not for running this
+// example from within the RiveScript project.
+
+var express = require("express"),
+ cookieParser = require('cookie-parser')
+ bodyParser = require("body-parser"),
+ RiveScript = require("../../lib/rivescript.js");
+
+var rs = new RiveScript({utf8: true});
+
+var mytest = function(location, callback) {
+ callback.call(this, null, location + " parsed");
+};
+
+
+rs.setSubroutine("mytest", function (rs, args) {
+ return new rs.Promise(function(resolve, reject) {
+ mytest(args.join(' '), function(error, data){
+ if(error) {
+ reject(error);
+ } else {
+ resolve(data);
+ }
+ });
+ });
+})
+
+var getWeather = function(location, callback) {
+ request.get({
+ url: "http://api.openweathermap.org/data/2.5/weather",
+ qs: {
+ q: location,
+ APPID: APPID
+ },
+ json: true
+ }, function(error, response) {
+ if (response.statusCode !== 200) {
+ callback.call(this, response.body);
+ } else {
+ callback.call(this, null, response.body);
+ }
+ });
+};
+
+
+rs.setSubroutine("getWeather", function (rs, args) {
+ return new rs.Promise(function(resolve, reject) {
+ getWeather(args.join(' '), function(error, data){
+ if(error) {
+ reject(error);
+ } else {
+ resolve(data.weather[0].description);
+ }
+ });
+ });
+});
+
+rs.setSubroutine("checkForRain", function(rs, args) {
+ return new rs.Promise(function(resolve, reject) {
+ getWeather(args.join(' '), function(error, data){
+ if(error) {
+ console.error('');
+ reject(error);
+ } else {
+ var rainStatus = data.rain ? 'yup :(' : 'nope';
+ resolve(rainStatus);
+ }
+ });
+ });
+});
+
+// Create a prototypical class for our own chatbot.
+var AsyncBot = function(onReady) {
+ var self = this;
+
+ if (APPID === 'change me') {
+ console.log('Error -- edit weatherman.js and provide the APPID for Open Weathermap.'.bold.yellow);
+ }
+
+ // Load the replies and process them.
+ //rs.loadDirectory("../brain", function() {
+ // rs.sortReplies();
+ // onReady();
+ //});
+
+
+ // Load the replies and process them.
+ rs.loadFile("weatherman.rive", function() {
+ rs.sortReplies();
+ onReady();
+ });
+
+ // This is a function for delivering the message to a user. Its actual
+ // implementation could vary; for example if you were writing an IRC chatbot
+ // this message could deliver a private message to a target username.
+ self.sendMessage = function(username, message) {
+ // This just logs it to the console like "[Bot] @username: message"
+ console.log(
+ ["[Brick Tamland]", message].join(": ").underline.green
+ );
+ };
+
+ // This is a function for a user requesting a reply. It just proxies through
+ // to RiveScript.
+ self.getReply = function(username, message, callback) {
+ return rs.replyAsync(username, message, self).then(function(reply){
+ callback.call(this, null, reply);
+ }).catch(function(error) {
+ callback.call(this, error);
+ });
+ }
+};
+
+// Create and run the example bot.
+var bot = new AsyncBot(function() {
+
+ // Set up the Express app.
+ var app = express();
+
+ app.use(cookieParser());
+
+ // Parse application/json inputs.
+ app.use(bodyParser.json());
+ app.set("json spaces", 4);
+
+ // Set up routes.
+ app.post("/reply", getReply);
+ app.get("/", showUsage);
+ app.get("*", showUsage);
+
+ // Start listening.
+ app.listen(2001, function() {
+ console.log("Listening on http://localhost:2001");
+ });
+});
+
+// POST to /reply to get a RiveScript reply.
+function getReply(req, res) {
+ // Get data from the JSON post.
+ var message = req.body.message;
+ var vars = req.body.vars;
+ var username;
+
+ if (req.cookies.username) {
+ username = req.cookies.username;
+ } else {
+ username = req.connection.remoteAddress;
+ console.log(username);
+ res.cookie('username', username, { maxAge: 100000 * 60 });
+ }
+
+ // Make sure username and message are included.
+ if (typeof(message) === "undefined") {
+ return error(res, "message is a required key");
+ }
+
+ // Copy any user vars from the post into RiveScript.
+ if (typeof(vars) !== "undefined") {
+ for (var key in vars) {
+ if (vars.hasOwnProperty(key)) {
+ rs.setUservar(username, key, vars[key]);
+ }
+ }
+ rs.setUservar(username, "username", username);
+ }
+
+ var reply = bot.getReply(username, message, function(error, reply){
+ if (error) {
+ res.json({
+ "status": "ko",
+ "reply": error,
+ "vars": vars
+ });
+ } else {
+ // Get all the user's vars back out of the bot to include in the response.
+ vars = rs.getUservars(username);
+
+ // Send the JSON response.
+ res.json({
+ "status": "ok",
+ "reply": reply,
+ "vars": vars
+ });
+ }
+ });
+
+};
+
+// All other routes shows the usage to test the /reply route.
+function showUsage(req, res) {
+ var egPayload = {
+ "username": "soandso",
+ "message": "Hello bot",
+ "vars": {
+ "name": "Soandso"
+ }
+ };
+ res.writeHead(200, {"Content-Type": "text/plain"});
+ res.write("Usage: curl -i \\\n");
+ res.write(" -H \"Content-Type: application/json\" \\\n");
+ res.write(" -X POST -d '" + JSON.stringify(egPayload) + "' \\\n");
+ res.write(" http://localhost:2001/reply");
+ res.end();
+}
+
+// Send a JSON error to the browser.
+function error(res, message) {
+ res.json({
+ "status": "error",
+ "message": message
+ });
+}
+
+
diff --git a/eg/json-server-async/package.json b/eg/json-server-async/package.json
new file mode 100644
index 0000000..befc5a5
--- /dev/null
+++ b/eg/json-server-async/package.json
@@ -0,0 +1,14 @@
+{
+ "name": "rive-weatherman",
+ "version": "1.0.0",
+ "description": "Your weather report",
+ "main": "weatherman.js",
+ "dependencies": {
+ "body-parser": "~1.14.2",
+ "colors": "^1.1.2",
+ "cookie-parser": "^1.4.3",
+ "elasticsearch": "^12.1.3",
+ "express": "~4.13.3",
+ "request": "^2.69.0"
+ }
+}
diff --git a/eg/json-server-async/weatherman.rive b/eg/json-server-async/weatherman.rive
new file mode 100644
index 0000000..723aa9e
--- /dev/null
+++ b/eg/json-server-async/weatherman.rive
@@ -0,0 +1,13 @@
+! sub how's = how is
+! sub is it going to = will it
+! sub is it gonna = will it
+! sub is it raining = will it rain
+
++ how is [the] weather in *
+- getWeather
+
++ will it rain in *
+- checkForRain
+
++ mytest1 *
+- mytest