Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mixing ES6 Promises with JQuery Promises

I have used $q (Angular.js) and would often return promises within a .then call. The effect was that the next .then call would wait for the previous promise to finish.

I am now using native es6 promises to try to 'promisify' a callback based library and I am unable to do so.

The problem is that the next value in the .then chain is a promise object, rather than the resolved value of that promise. It calls the next .then value before the promise is resolved, simply returning the last return value.

Is there anyway to wait for the previous promise to resolve?

Example:

$.ajax({
    url: "//localhost:3000/api/tokens",
    type: "POST",
    data: JSON.stringify({
      user: {
        email: '[email protected]',
        password: 'password123'
      }
    }),
    contentType: "application/json"
})
.then(data => data.token.encoded)         // OK
.then(token => Farmbot({ token: token })) // OK
.then(function(bot){                      // OK
  return new Promise(function(resolve, reject) {
    bot.connect(function(){ resolve(bot); });
  });
}, errorr)
.then(function(bot){ // NOT OK!
  // passes in an unresolved promise object, which is useless.
  //
  bot; // => {[[PromiseStatus]]: "pending", [[PromiseValue]]: undefined}
});

My question is:

Do ES6 promises wait for the previous .then's promise to resolve?

like image 452
Rick Avatar asked Mar 28 '16 03:03

Rick


2 Answers

The issue stems from trying to use a native Promise within a chain of Deferred promises.

Currently (jQuery 1.8 – 2.x), jQuery's definition of .then() only recognizes the library's own type in its support for chaining.

So, you could return a $.Deferred() promise instead:

// ...
.then(function(bot){
  return $.Deferred(function(defer) {
    bot.connect(function(){ defer.resolve(bot); });
  });
}, errorr)
// ...

Or, you can use Promise.resolve(thenable) to convert the initial $.Deferred() given by $.ajax() to a native Promise so that you're using the native .then() throughout the chain, which will recognize a new Promise() being returned (as well as a $.Deferred()):

Promise.resolve($.ajax({
  // ...
}))
.then(data => data.token.encoded)
// ...

Or, you can try jQuery 3.0, currently in beta:

jQuery.Deferred is now Promises/A+ compatible

jQuery.Deferred objects have been updated for compatibility with Promises/A+ and ES2015 Promises, verified with the Promises/A+ Compliance Test Suite. [...]

With it, your original snippet should work as you expected without any revision.

like image 163
Jonathan Lonowski Avatar answered Oct 13 '22 10:10

Jonathan Lonowski


Do ES6 promises wait for the previous .then's promise to resolve?

Put it this way: an ES6 promise never, ever calls a .then(onFulfilled) function with a promise or thenable object. Onfulfilled listeners are only called with non-promise values.

Converting jQuery "thenable" objects into ES6 Promises before trusting them may solve the problem:

var jqPromise = $.ajax({
    url: "//localhost:3000/api/tokens",
    type: "POST",
    data: JSON.stringify({
      user: {
        email: '[email protected]',
        password: 'password123'
      }
    }),
    contentType: "application/json"
})
.then(data => data.token.encoded)         // OK
.then(token => Farmbot({ token: token })) // OK

; 

var es6Promise = Promise.resolve(jqPromise); // convert to ES6 promise

es6Promise.then(function(bot){                      // OK
  return new Promise(function(resolve, reject) {
    bot.connect(function(){ resolve(bot); });
  });
}, errorr)
.then(function(bot){ // will not pass in unfulfilled promise

  // process bot value

});
like image 45
traktor Avatar answered Oct 13 '22 10:10

traktor