Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is javascript Promise API more convoluted than it needs to be?

I think I have finally managed to bend my mind around javascript/ES6 Promises, for the most part. It wasn't easy! But there's something that's baffling me about the design.

Why does the Promise constructor take a callback? Given that the callback is called immediately, couldn't the caller just execute that code instead, thereby avoiding one unnecessary level of mind-bending "don't call me, I'll call you"?

Here's what I think of as the prototypical example of Promise usage, copied from Jake Archibald's Javascript Promises tutorial http://www.html5rocks.com/en/tutorials/es6/promises/#toc-promisifying-xmlhttprequest , with comments stripped.

It's a Promise-based wrapper for an XMLHttpRequest GET request:

function get(url) {
  return new Promise(function(resolve, reject) {
    var req = new XMLHttpRequest();
    req.open('GET', url);
    req.onload = function() {
      if (req.status == 200) {
        resolve(req.response);
      }
      else {
        reject(Error(req.statusText));
      }
    };
    req.onerror = function() {
      reject(Error("Network Error"));
    };
    req.send();
  });
}

For me, the above code would be much easier to understand if it were rewritten as follows, using a very slightly different kind of promise that I'm imagining, having a no-arg constructor and resolve/reject methods:

function get(url) {
  var promise = new MyEasierToUnderstandPromise();
  var req = new XMLHttpRequest();
  req.open('GET', url);
  req.onload = function() {
    if (req.status == 200) {
      promise.resolve(req.response);
    }
    else {
      promise.reject(Error(req.statusText));
    }
  };
  req.onerror = function() {
    promise.reject(Error("Network Error"));
  };
  req.send();
  return promise;
}

MyEasierToUnderstandPromise is not too hard to implement in terms of Promise. At first I tried making it an actual subclass of Promise, but for some reason I couldn't get that to work; so instead I implemented it as a simple factory function, which returns a plain old Promise object with a couple of extra functions attached that behave like member functions:

function NewMyEasierToUnderstandPromise() {
  var resolveVar;
  var rejectVar;
  var promise = new Promise(function(resolveParam, rejectParam) {
    resolveVar = resolveParam;
    rejectVar = rejectParam;
  });
  promise.resolve = resolveVar;
  promise.reject = rejectVar;
  return promise;
};

So, why isn't Promise designed like this? I think if it was, it would have helped me to understand Promises a lot quicker-- I bet it would have cut my learning time in half.

I know a lot of smart people had a hand in making the Promise API, and everyone seems to be generally happy and proud of it, so I'm wondering what they were thinking.

like image 576
Don Hatch Avatar asked Feb 13 '16 03:02

Don Hatch


2 Answers

Your version is not exception-safe, whereas Promises/A+ are safe since they are caught by the Promise constructor.

like image 130
Amit Avatar answered Oct 21 '22 18:10

Amit


Promises are intended to be used as values. The ES constructor approach encapsulates the creation of the Promise, which can then be passed around like a value. When that value is passed around, the consumer of that value has no need for resolve and reject and so those functions should not be part of the public API.

(and all that stuff about exception handling and chaining)

like image 36
rich remer Avatar answered Oct 21 '22 20:10

rich remer