JS Promise - instantly retrieve some data from a function that returns a Promise JS Promise - instantly retrieve some data from a function that returns a Promise ajax ajax

JS Promise - instantly retrieve some data from a function that returns a Promise


Okay, let's address your bounty note first.

[Hopefully I'll be able to grant the points to someone who says more than "Don't use promises"... ]

Sorry, but the answer here is: "Don't use promises". ES6 Promises have three possible states (to you as a user): Pending, Resolved and Rejected (names may be slightly off).

There is no way for you to see "inside" of a promise to see what has been done and what hasn't - at least not with native ES6 promises. There was some limited work (in other frameworks) done on promise notifications, but those did not make it into the ES6 specification, so it would be unwise of you to use this even if you found an implementation for it.

A promise is meant to represent an asynchronous operation at some point in the future; standalone, it isn't fit for this purpose. What you want is probably more akin to an event publisher - and even that is asynchronous, not synchronous.

There is no safe way for you to synchronously get some value out of an asynchronous call, especially not in JavaScript. One of the main reasons for this is that a good API will, if it can be asynchronous, will always be asynchronous.

Consider the following example:

const promiseValue = Promise.resolve(5)promiseValue.then((value) => console.log(value))console.log('test')

Now, let's assume that this promise (because we know the value ahead of time) is resolved synchronously. What do you expect to see? You'd expect to see:

> 5> test

However, what actually happens is this:

> test> 5

This is because even though Promise.resolve() is a synchronous call that resolves an already-resolved Promise, then() will always be asynchronous; this is one of the guarantees of the specification and it is a very good guarantee because it makes code a lot easier to reason about - just imagine what would happen if you tried to mix synchronous and asynchronous promises.

This applies to all asynchronous calls, by the way: any action in JavaScript that could potentially be asynchronous will be asynchronous. As a result, there is no way for you do any kind of synchronous introspection in any API that JavaScript provides.

That's not to say you couldn't make some kind of wrapper around a request object, like this:

function makeRequest(url) {  const requestObject = new XMLHttpRequest()  const result = {  }  result.done = new Promise((resolve, reject) => {    requestObject.onreadystatechange = function() {      ..    }  })  requestObject.open(url)  requestObject.send()  return requestObject}

But this gets very messy, very quickly, and you still need to use some kind of asynchronous callback for this to work. This all falls down when you try and use Fetch. Also note that Promise cancellation is not currently a part of the spec. See here for more info on that particular bit.

TL:DR: synchronous introspection is not possible on any asynchronous operation in JavaScript and a Promise is not the way to go if you were to even attempt it. There is no way for you to synchronously display information about a request that is on-going, for example. In other languages, attempting to do this would require either blocking or a race condition.


Well. If using angular you can make use of the timeout parameter used by the $http service if you need to cancel and ongoing HTTP request.

Example in typescript:

interface ReturnObject {  cancelPromise: ng.IPromise;  httpPromise: ng.IHttpPromise;}@Service("moduleName", "aService")class AService() {  constructor(private $http: ng.IHttpService              private $q: ng.IQService) { ; }  doSomethingAsynch(): ReturnObject {    var cancelPromise = this.$q.defer();    var httpPromise = this.$http.get("/blah", { timeout: cancelPromise.promise });    return { cancelPromise: cancelPromise, httpPromise: httpPromise };  }}@Controller("moduleName", "aController")class AController {  constructor(aService: AService) {    var o = aService.doSomethingAsynch();    var timeout = setTimeout(() => {      o.cancelPromise.resolve();    }, 30 * 1000);    o.httpPromise.then((response) => {      clearTimeout(timeout);      // do code    }, (errorResponse) => {      // do code    });  }}

Since this approach already returns an object with two promises the stretch to include any synchronous operation return data in that object is not far.

If you can describe what type of data you would want to return synchronously from such a method it would help to identify a pattern. Why can it not be another method that is called prior to or during your asynchronous operation?


You can kinda do this, but AFAIK it will require hacky workarounds. Note that exporting the resolve and reject methods is generally considered a promise anti-pattern (i.e. sign you shouldn't be using promises). See the bottom for something using setTimeout that may give you what you want without workarounds.

let xhrRequest = (path, data, method, success, fail) => {  const xhr = new XMLHttpRequest();  // could alternately be structured as polymorphic fns, YMMV  switch (method) {    case 'GET':      xhr.open('GET', path);      xhr.onload = () => {          if (xhr.status < 400 && xhr.status >= 200) {            success(xhr.responseText);            return null;          } else {            fail(new Error(`Server responded with a status of ${xhr.status}`));            return null;          }        };        xhr.onerror = () => {          fail(networkError);          return null;        }        xhr.send();        return null;      }      return xhr;    case 'POST':      // etc.      return xhr;    // and so on...};// can work with any function that can take success and fail callbacksclass CancellablePromise {  constructor (fn, ...params) {    this.promise = new Promise((res, rej) => {      this.resolve = res;      this.reject = rej;      fn(...params, this.resolve, this.reject);      return null;    });  }};let p = new CancellablePromise(xhrRequest, 'index.html', null, 'GET');p.promise.then(loadPage).catch(handleError);// times out after 2 secondssetTimeout(() => { p.reject(new Error('timeout')) }, 2000);// for an alternative version that simply tells the user when things // are taking longer than expected, NOTE this can be done with vanilla // promises:let timeoutHandle = setTimeout(() => {  // don't use alert for real, but you get the idea  alert('Sorry its taking so long to load the page.');}, 2000);p.promise.then(() => clearTimeout(timeoutHandle));