Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to cast jQuery $.ajax calls to Bluebird promises without the deferred anit-pattern

Tags:

Right now I use promise.deferred in a core file. This allows me to resolve promises at a central location. I've been reading that I may be using an anti-pattern and I want to understand why it is bad.

so in my core.js file I have functions like this:

var getMyLocation = function(location) {    
    var promiseResolver = Promise.defer();

    $.get('some/rest/api/' + location)
        .then(function(reponse) {
            promiseResolver.resolve(response);
        )}
        .catch(function(error) {
            promiseResolver.reject(error);
        });

     return promiseResolver.promise;
}

And then in my getLocation.js file I have the following:

var core = require('core');
var location = core.getMyLocation('Petersburg')
    .then(function(response) {
        // do something with data
    }).catch(throw error);

After reading the Bluebird docs and many blog posts about the deferred anti-pattern I wonder if this pattern is practical. I could change this to the following:

core.js

var getMyLocation = function(location) {
    var jqXHR = $.get('some/rest/api/' + location);
    return Promise.resolve(jqXHR)
        .catch(TimeoutError, CancellationError, function(e) {
            jqXHR.abort();
            // Don't swallow it
            throw e;
        });

getLocation.js

var location = core.getMyLocation('Petersburg')
    .then(function(response) {
        // do something
    })
    .catch(function(error) {
        throw new Error();
    });

I guess I'm confused by what is the best way to have a central library that handles xhr requests using jquery for the calls, but Bluebird for the promises.

like image 600
Jeff Avatar asked Jun 19 '14 19:06

Jeff


2 Answers

You can call Promise.resolve on a jQuery thenable and have Bluebird assimilate it:

var res = Promise.resolve($.get(...)); // res is a bluebird Promise

You can also return jQuery promises directly inside a Bluebird chain and have it assimilate it.

myBluebirdApi().then(function(){
    return $.get(...);
}).then(function(result){
    // The jQuery thenable was assimilated
});

Your code below is close, but you don't need to catch TimeoutError since jQuery ajax won't throw those. As for catching cancellation error. This is best practice anyway for what you're doing if you ever expect to need to cancel the request.

like image 170
Benjamin Gruenbaum Avatar answered Sep 18 '22 15:09

Benjamin Gruenbaum


To convert a thenable to a Bluebird promise, you can use can call Promise.resolve like this:

var promise = Promise.resolve($.getJSON(...));

Bonus section:

Most JQuery AJAX functions are thenable, but for your information, if you want to convert a function that expects a callback to a promise, you can use Promise.fromNode. The callback will be called with the arguments err, result as is convention in the Node.js world:

var promise = Promise.fromNode(function (callback) { request(url, callback); });

If the callback doesn't expect a potential error for its first argument, you can work around that:

var promise = Promise.fromNode(function (callback) {
  FB.api(url, function(response) { callback(response ? response.error : "no response", response); });
});
like image 28
Flimm Avatar answered Sep 19 '22 15:09

Flimm