Bill Noble Bill Noble - 2 months ago 24
TypeScript Question

Chaining promises to cope with asynchronous promise from a this.storage.get() call

I am struggling to cope with handling asynchronous retrieval of data from local storage in my app, which is written in Angular2/ionic2.

The code I have is as follows:



request(args) {
var headers = new Headers();
headers.append('Content-Type', 'application/json');

// Retrieve the token from the local storage, if available
this.storage.get('token').then((value) => {
headers.append('Authorization', 'Token ' + value);
});

args = args || {};

var url = this.API_URL + args.url;
var method = args.method;
var data = args.data || {};

var options = new RequestOptions({headers: headers, withCredentials: this.use_session});

return new Promise((resolve,reject) => {

if (method == 'GET') {
this.http.get(url, options)
.map(res => res.text())
.subscribe(
(data:any) => {
resolve(data);
},
(err:any) => {
data = {};
data['status'] = 0;
data['non_field_errors'] = ["Could not connect to server. Please try again."];
data['error'] = err;
reject();
},
() => {
}
);
}
});
}





The code that calls the request function looks like this:



authenticationStatus(){
return this.request({
'method': "GET",
'url': "/user/"
})
.then((data:any) => {
console.log('****** authenticationStatus: GET /user/ SUCCESS');
});
}





And this is in turn called like this:



this.djangoAuth.authenticationStatus()
.then((data:any) => {
this.loggedIn = true;
this.enableMenu(true);
this.root = PhotoPage;
},
(err:any)=>{
this.loggedIn = false;
this.enableMenu(false);
this.root = LoginPage;
});





I have difficulty wrapping my head around nested promises and cannot work out how to amend the code above so that the result of the
this.storage.get("token")
call has added data to the header before it is used in the code below. I think I need to chain the promises in some way but cannot work out exactly how.

Answer

Yes you need to chain two promises - it's actually easy. All you have to do is to return second promise from the callback that you pass to the first promise then() method.

You can rewrite your code like this (I moved the code that adds headers to separate function - I think it's more readable that way)

// returns a promise that resolves to headers
createHeaders() {
  var headers = new Headers();
  headers.append('Content-Type', 'application/json');

  // Retrieve the token from the local storage, if available
  return this.storage.get('token').then((value) => {
    headers.append('Authorization', 'Token ' + value);
    return headers;
  });
}

request(args) {

  return this.createHeaders().then(headers => {
    args = args || {};

    var url = this.API_URL + args.url;
    var method = args.method;
    var data = args.data || {};

    var options = new RequestOptions({headers: headers, withCredentials: this.use_session});

    return new Promise((resolve,reject) => {

      if (method == 'GET') {
        this.http.get(url, options)
          .map(res => res.text())
          .subscribe(
            (data:any) => {
              resolve(data);
            },
            (err:any) => {
              data = {};
              data['status'] = 0;
              data['non_field_errors'] = ["Could not connect to server. Please try again."];
              data['error'] = err;
              reject();
            },
            () => {
            }
          );
      }
    });
  });
}

The most difficult thing in all this is not to forget every and each necessary return.

Also, I noticed that your Promise will never complete if it receives any method other than GET - you might want to add else part with reject

if (method == 'GET') {
    ....
} else {
    reject(new Error('invalid method:' + method));
}