Eliminar promesas anidadas

Resuelto Tarrence asked hace 10 años • 2 respuestas

Soy nuevo en las promesas y en la escritura de código de red mediante solicitudes y promesas en NodeJS.

Me gustaría eliminar estas promesas anidadas y encadenarlas, pero no estoy seguro de cómo hacerlo o si es el camino correcto a seguir.

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);
                    });
            });
    });
};

Este es el código de solicitud:

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);
      });
    });
}

Cualquier idea sería apreciada.

Tarrence avatar Feb 25 '14 04:02 Tarrence
Aceptado

En cada thendevolución de llamada, deberás devolver la nueva promesa:

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);
      });
};

La promesa devuelta por la .then()llamada se resolverá con el valor de la promesa "interna", de modo que pueda encadenarlas fácilmente.

Patrón genérico:

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
Bergi avatar Feb 24 '2014 22:02 Bergi

Promise.prototype.thenestá diseñado para devolver otra promesa, para que puedas encadenarlas.

La función de controlador pasada .then()puede devolver un valor normal , como un número, una cadena u un objeto, y este valor se pasará al siguiente controlador de .then().

Una opción es tener boxViewerRequestSyncuna función síncrona que devuelva un responseobjeto:

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);
    })

Pero, por supuesto, boxViewerRequestes asincrónico y, en su lugar, devuelve una promesa. En ese caso, la función de controlador pasada .then()también podría devolver un archivoPromise . Esta nueva promesa se ejecuta de forma sincrónica y, una vez resuelta/rechazada, su resultado se pasa al siguiente controlador.

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);
    })

Hacer un seguimiento de todas las promesas es confuso, pero la conclusión es la siguiente: siempre devolveráPromise.prototype.then un objeto Promesa, pero la función de controlador pasada puede devolver cualquier cosa, incluso indefinida, o incluso otra Promesa. Luego, ese valor, o el valor de la Promesa resuelta, se pasa a la siguiente función de controlador.Promise.prototype.then

chharvey avatar Feb 05 '2018 17:02 chharvey