Cellendhyll
Cellendhyll

Reputation: 63

Promise not waiting to resolve

I just heard about promises and it sounds... promising (ok I'm leaving).

I would expect the following code to show:
- before initVariables
- before initVariables - someName
- before initVariables - someName - after initElements

But instead I got:
- before initVariables
- before initVariables
- before initVariables - after initElements

Anybody to help me? :-)
Thx all! :D

var url = "https://opendata.paris.fr/api/records/1.0/search/?dataset=stations-velib-disponibilites-en-temps-reel&rows=100&facet=banking&facet=bonus&facet=status&facet=contract_name"

var test = Object.create(MyTestClass);
console.log(test.testVariable);

let loadVariables = new Promise(function(resolve, reject) {
  test.initVariables(url);
  resolve(test);
  console.log(test.testVariable);
});

loadVariables.then(function(test){
  test.initElements();
});
console.log(test.testVariable);

And MyTestClass is defined by:

var MyTestClass = {

    testVariable: "before initVariables",

    initVariables: function(url) {
        $.getJSON(url, function (result) {
            this.testVariable += " - " + result.records[0].fields.name;
        });
    },

    initElements: function() {
        this.testVariable += " - after initElements";
    }
}

Upvotes: 0

Views: 1354

Answers (3)

traktor
traktor

Reputation: 19301

To explain the observed result, the expected result is missing the effect of asynchronous actions. Here are some rules that may help:

  1. Promise executor functions are called synchronously when a new promise is created. When execution continues after a new Promise( executor) statement or expression, the executor function has returned.

  2. Promise reaction handlers, supplied by calling then or catch methods on a promise, are executed asynchronously, in a thread of their own, after the promise they were called on becomes settled.

  3. Assume that the $.getJSON request makes a call back asynchronously after a HTTP GET operation has taken place.

Walking through the code:

//**  The top of code executing in a thread, say "T1":

var url = "https://www.example.com"

var test = Object.create(MyTestClass);
//** here test.testVariable inherits "before initVariables"

console.log(test.testVariable);

let loadVariables = new Promise(function(resolve, reject) {
  test.initVariables(url);
//** here an asynchronous $getJSON request has been initiated but not completed

  resolve(test);
//**  here the Promise has been synchronously fulfilled,
//**  during execution of this executor function,  with the test object.

  console.log(test.testVariable);

//** here test.testVariable inherits "before initVariables"
//**  because we are still running synchronously in thread "T1" from the top.

});

loadVariables.then(function(test){
  test.initElements();
//** this is some time later, not in thread "T1", because
//** then and catch callbacks are made asynchronously.

});
console.log(test.testVariable);

  // here thread "T1" is still executing,
  // testVariable has not been updated yet.

Note that synchronously fulfilling the created promise with the test object was not useful in the end. Waiting for the GET request to call back with result data requires reconsideration of the approach to take.

Upvotes: 0

guest271314
guest271314

Reputation: 1

Promise constructor is not necessary, as $.getJSON() returns a jQuery promise object which exposes .then function.

$.getJSON() is not returned from function at javascript at Question. this is jqxhr jQuery promise object unless set at context of $.ajax() calls. You can use Function.prototype.bind() or $.proxy() to set this at .then() chained to test.initVariables, where $.getJSON(url) call is returned from function.

var MyTestClass = {

  testVariable: "before initVariables",

  initVariables: function(url) {
    return $.getJSON(url)
  },

  handleResult: function(result) {
    this.testVariable += " - " + result.records[0].fields.name;
    console.log(this.testVariable);
  },

  initElements: function() {
    this.testVariable += " - after initElements";
    console.log(this.testVariable)
  }
}

var url = "https://opendata.paris.fr/api/records/1.0/search/?dataset=stations-velib-disponibilites-en-temps-reel&rows=100&facet=banking&facet=bonus&facet=status&facet=contract_name";

var test = Object.create(MyTestClass);

console.log(test.testVariable);

var loadVariables = test.initVariables(url);

loadVariables
.then($.proxy(test.handleResult, test))
.then($.proxy(test.initElements, test))
.fail(function(jqxhr, textStatus, errorThrown) {
  console.log(textStatus, errorThrown);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js">
</script>

Upvotes: 0

user128511
user128511

Reputation:

Promises run immediately. The only way they don't resolve/reject immediately is if you do something inside them (like call $.getJSON) and only call resolve/reject based on the callback from $.getJSON.

function getJSONP(url) {
  return new Promise(function(resolve, reject) {
     $.getJSON(url, resolve);
  }
}

or

function getJSONP(url) {
  return new Promise(function(resolve, reject) {
    $.getJSON(url, function(result) { 
       resolve(result);
    });
  }
}

It that makes it clearer

You can then do

 getJSONP(someURL).then(function(result) {
   console.log(result);
 });

Upvotes: 4

Related Questions