By Tarrence


2014-02-24 21:16:31 8 Comments

I'm new to promises and writing network code using requests and promises in NodeJS.

I would like to remove these nested promises and chain them instead, but I'm not sure how I'd go about it/whether it is the right way to go.

exports.viewFile = function(req, res) {
var fileId = req.params.id;
boxContentRequest('files/' + fileId + '/content', req.user.box.accessToken)
    .then(function(response) {
        boxViewerRequest('documents', {url: response.request.href}, 'POST')
            .then(function(response) {
                boxViewerRequest('sessions', {document_id: response.body.id}, 'POST')
                    .then(function(response) {
                        console.log(response);
                    });
            });
    });
};

This is the request code:

var baseContentURL = 'https://api.box.com/2.0/';
var baseViewerURL = 'https://view-api.box.com/1/';

function boxContentRequest(url, accessToken) {
    return new Promise(function (resolve, reject) {
            var options = {
                url: baseContentURL + url,
                headers: {
                    Authorization: 'Bearer ' + accessToken,
                }
            };
      request(options, function (err, res) {
        if (err) {
          return reject(err);
        } else if (res.statusCode !== 200) {
          err = new Error("Unexpected status code: " + res.statusCode);
          err.res = res;
          return reject(err);
        }
        resolve(res);
      });
    });
}

function boxViewerRequest(url, body, method) {
    return new Promise(function (resolve, reject) {
            var options = {
                method: method,
                url: baseViewerURL + url,
                headers: {
                    Authorization: 'Token ' + config.box.viewerApiKey
                },
                json: body
            };
      request(options, function (err, res, body) {
        if (err) {
          return reject(err);
        } else if (res.statusCode !== 200 && res.statusCode !== 201 && res.statusCode !== 202) {
          err = new Error("Unexpected status code: " + res.statusCode);
          err.res = res;
          return reject(err);
        }
        resolve(res, body);
      });
    });
}

Any insight would be appreciated.

2 comments

@chharvey 2018-02-05 17:59:33

Promise.prototype.then is designed to return another promise, so that you can chain them.

The handler function passed to .then() can return a normal value, like a number or string or object, and this value will get passed on to the next handler of .then().

One option is to have boxViewerRequestSync be a synchronous function that returns a response object:

boxContentRequest('files/' + fileId + '/content', req.user.box.accessToken)
    .then(function(response) {
        return boxViewerRequestSync('documents', {url: response.request.href}, 'POST')
    })
    .then(function(response) { // this `response` is returned by `boxViewerRequestSync`
        return boxViewerRequestSync('sessions', {document_id: response.body.id}, 'POST')
    })
    .then(function(response) {
        console.log(response);
    })

But of course your boxViewerRequest is asynchronous and returns a promise instead. In that case, the handler function passed to .then() could also return a completely unrelated Promise. This new promise is executed synchronously, and once it is resolved/rejected, its result is passed on to the next handler.

boxContentRequest('files/' + fileId + '/content', req.user.box.accessToken)
    .then(function(response) {
        return boxViewerRequest('documents', {url: response.request.href}, 'POST')
    })
    .then(function(response) { // this `response` is the result of resolving the promise returned by `boxViewerRequest`
        return boxViewerRequest('sessions', {document_id: response.body.id}, 'POST')
    })
    .then(function(response) {
        console.log(response);
    })

Keeping track of all the promises is confusing, but the bottom line is this: Promise.prototype.then will always return a Promise object, but the handler function passed to Promise.prototype.then can return anything, even undefined, or even another Promise. Then that value, or the value of the resolved Promise, is passed to the next handler function.

@Bergi 2014-02-24 22:55:55

From every then callback, you will need to return the new promise:

exports.viewFile = function(req, res) {
    var fileId = req.params.id;
    boxContentRequest('files/' + fileId + '/content', req.user.box.accessToken)
      .then(function(response) {
          return boxViewerRequest('documents', {url: response.request.href}, 'POST');
      })
      .then(function(response) {
          return boxViewerRequest('sessions', {document_id: response.body.id}, 'POST');
      })
      .then(function(response) {
          console.log(response);
      });
};

The promise that is returned by the .then() call will then resolve with the value from the "inner" promise, so that you easily can chain them.

Generic pattern:

somePromise.then(function(r1) {
    return nextPromise.then(function(r2) {
        return anyValue;
    });
}) // resolves with anyValue

     ||
    \||/
     \/

somePromise.then(function(r1) {
    return nextPromise;
}).then(function(r2) {
    return anyValue;
}) // resolves with anyValue as well

Related Questions

Sponsored Content

43 Answered Questions

[SOLVED] How do I remove a property from a JavaScript object?

96 Answered Questions

[SOLVED] How can I remove a specific item from an array?

  • 2011-04-23 22:17:18
  • Walker
  • 6947020 View
  • 8507 Score
  • 96 Answer
  • Tags:   javascript arrays

18 Answered Questions

[SOLVED] Remove element by id

  • 2010-08-01 18:47:14
  • Zaz
  • 1532928 View
  • 1150 Score
  • 18 Answer
  • Tags:   javascript dom

29 Answered Questions

43 Answered Questions

[SOLVED] Remove empty elements from an array in Javascript

17 Answered Questions

19 Answered Questions

[SOLVED] How do I convert an existing callback API to promises?

1 Answered Questions

[SOLVED] axios return error on status 200

Sponsored Content