Botkit: Promise-based API

Created on 15 Sep 2016  路  5Comments  路  Source: howdyai/botkit

I know, it is radical changes, but why not promisify whole botkit api?
Promises are now part of javascript, why do not use it? Yes, node apis are still using callbacks, but most of them can be automatic promisified, unlike such frameworks. I think support of promise-based api is preferable, because for people who don't use promises it will be more easy to deal with promisified api, than manually promisify callback-based api for people who use promises.

Now it looks like:

bot.sendWebhook({
  text: 'This is an incoming webhook',
  channel: '#general',
},function(err,res) {
  if (err) {
    handleError(error);
  } else {
    handleRes(res);
  }
});

with promises it will be:

bot.sendWebhook({
  text: 'This is an incoming webhook',
  channel: '#general',
})
.then(function(res) {
  handleRes(res);
})
.catch(function(error) {
  handleError(error);
});

which is more simple then manually promisify each call:

new Promise(function(resolve, reject) {
  bot.sendWebhook({
    text: 'This is an incoming webhook',
    channel: '#general',
  },function(err,res) {
    if (err) {
     reject(error);
    } else {
      resolve(res);
    }
  });
})
.then(function(res) {
  return handleRes(res);
})
.catch(function(error) {
  return handleError(error);
});

Seems like people really want it:

278

https://github.com/colestrode/botkit-promise-storage

Slack-related question

All 5 comments

I've just used Botkit to build a Slack App from the ground up over the last few weeks. Having to promisify and wrap all the functions I'm using so that I can avoid screwing with callbacks (I'm actually using Promise.coroutine from bluebird) has been tricky. In most cases, when bot is passed in to a handler, it's not the same reference as the one I originally promisified after I received it from the startRTM function; so, I have to store a reference to the promisified bot in a Map and look that one up. I've just been crossing my fingers that nothing changes over time in those bot objects.

Anyhow, a good compromise to handle this would be following the example that I'm seeing more and more JavaScript codebases follow:

If a callback is passed in, call the callback; otherwise, return a Promise.

It's a very fluid approach in which pretty much everybody wins. If you wanted to add bluebird as a dependency, it even has a function to help with that: .asCallback. This is only a helper for making your public API flexible; your internals would still have to be refactored to operate with Promises.

Here's an example. Let's say you've got some functions getCar and getUser that are async and return a Promise, and you use those to implement a public API function getUserCar. It could look like this:

exports.getUserCar = function (user, cb) {
    let car;

    return getCar({ user })
        .then(function (c) {
            car = c;

            return getUser(c.user);
        })
        .then(function (u) {
            car.user = u;

            return car;
        })
        .asCallback(cb);
};

It's even possible to use coroutines with .asCallback, though it requires an intermediary helper function (Promise.coroutine returns a function, not a promise).

Anyhow, I'm excited to see what the response to this will be. Thanks for the consideration!

If a callback is passed in, call the callback; otherwise, return a Promise.

That's exactly what my above-referenced PR #278 does for the Slack Web API, without breaking the existing API at all. I'm quite disappointed that it was never merged.

@mjesuele Well, hopefully the author can still use your work, if s/he/they decide to go forward with this.

Are there any plans regarding this?

Not currently.

Was this page helpful?
0 / 5 - 0 ratings