3

In my vanilla js project I have a following chain of promises:

API.POST({link to login endpoint}, "email=foo&pass=bar")
.then(() => User.initiate(API))
.then(() => console.log(User.name || 'wat'));

API object has POST and GET methods both looking the same, except the request type:

GET (url, params = null) {
    console.log("GET start");
    return new Promise((resolve,reject) => {
        this._request('GET', url, params)
        .then(result => resolve(result));
    });
}

POST (url, params = null) {
    return new Promise((resolve,reject) => {
        this._request('POST', url, params)
        .then(result => resolve(result));
    });
}

... and a _request method responsible for sending the request:

_request (type, url, params = null) {
    return new Promise((resolve, reject) => {
        var xhr = new XMLHttpRequest();
        xhr.responseType = 'json';
        xhr.open(type,url,true);
        xhr.withCredentials = true;
        xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded; charset=UTF-8");
        xhr.onload = function(){
            if (this.status == 200) {
                console.log(type + ' done');
                resolve(this.response);
            } else {
                reject(this.response._status.code);
            }
        };
        
        xhr.send(params);
        
    });
}

App's User object provides "initiate" method that calls the API to check if user is logged in. With positive response API returns an _embedded.user object, that in the next step is used to populate app's User properties:

initiate(API) {
    API.GET({link to authorization check endpoint})
    .then(authData => this.regenerate(authData._embedded.user));
},
regenerate(userData) {
    this.name = userData.name;

    // and so on, for each User property
    
    return new Promise((resolve,reject) => {
        resolve(this);
    });
}

What I expect to happen is:

  1. Request to API to log in is sent (this is just to skip the actual logging in process that is irrelevant to the current work)
  2. API returns cookie allowing for further testing as an logged user
  3. Request to API is sent to ask if user is authenticated
  4. API responds with confirmation and _embedded.user object
  5. App's User object's properties get populated by data from API response
  6. User.name is logged in console

Step 6 though fires between steps 3. and 4. and I can't find the reason why. My console looks following (notice the console.logs in API object code above):

POST done

GET start

wut

GET done

What can be the reason of this? Thanks in advance.

Community
  • 1
  • 1
EmilBogumil
  • 103
  • 1
  • 7
  • 1
    Avoid the [`Promise` constructor antipattern](https://stackoverflow.com/q/23803743/1048572?What-is-the-promise-construction-antipattern-and-how-to-avoid-it) in `GET` and `POST`! Also it doesn't look like `regenerate` should return a promise. – Bergi May 30 '18 at 21:04

1 Answers1

6

Missing a return in initiate()

initiate(API) {
   return API.GET({link to authorization check endpoint})
   //^^ return the promise
    .then(authData => this.regenerate(authData._embedded.user));
}

Also using a promise anti-pattern in your GET and POST methods. There is no need to create a new promise in each since _request() already returns a promise

All you need is:

GET (url, params = null) {
    return this._request('GET', url, params);
}

POST (url, params = null) {
    return this._request('POST', url, params);       
}

For more detailed explanation see What is the explicit promise construction antipattern and how do I avoid it?


Rather than using XMLHttpRequest you may also want to look at using the more recent fetch() API which has built in promises and better error handling

charlietfl
  • 170,828
  • 13
  • 121
  • 150