将具有数据依赖性的回调链转换为promise

时间:2016-06-15 14:24:58

标签: javascript callback promise

我有一个浏览器的异步javascript应用程序,我想使用promises。 每个异步调用都具有相同的模式,可以在promise中进行转换。 我的问题是我在回调链中有数据依赖。 一些简化的代码:

getAlpha(function(err, alpha){
        if(err) {handleError(err); return;}
        getBeta(function(err, beta){
                if(err) {handleError(err); return;}
                getOmega(function(err, omega){
                        if(err) {handleError(err); return;}
                        getEpsilon(beta, function(err, epsilon){ // depends on beta
                                if(err) {handleError(err); return;}
                                getDelta(beta, function(err, delta){ // depends on beta
                                        if(err) {handleError(err); return;}
                                        console.log(alpha + beta + omega + epsilon + delta);
                                });
                        });
                });
        });
});

我在这里看到两个承诺的问题:

  1. 回调调用之间存在数据依赖关系。 getEpsilon和getDelta取决于beta值。

  2. 我需要在第一次调用和其他回调的最后一次回调中收集所有数据。

  3. 我在这里看http://stuk.github.io/promise-me/一些例子。 "捕获变量"示例解决了这两个问题,但它会产生相同的回调阶梯,我们看到它没有承诺。

    其他方法是使数据对象存储所有承诺返回。它看起来像这样:

    var res = {};
    getAlpha().then(function(alpha){
        res.alpha = alpha;
        return getBeta();
    }).then(function(beta){
        res.beta = beta;
        return getOmega();
    }).then(function(omega){
        res.omega = omega;
        return getEpsilon(res.beta);
    }).then(function(epsilon){
        res.epsilon = epsilon;
        return getDelta(res.beta);
    }).then(function(delta){
        res.delta = delta;
        console.log([res.alpha, res.beta, res.omega, res.epsilon, res.delta].join(' '));
    }).catch(function(err){
        handleError(err);
    });
    

    我想知道是否有可能在没有嵌套调用和数据容器的情况下解决这个问题。

    UPD 1。我很抱歉,但我的第一个承诺解决方案根本不起作用,所以我制作了正确的版本。

    UPD 2。在原始代码中,每个get-LETTER调用都是一个GET http请求,因此这些调用可以并行工作。

    UPD 3。感谢robertklep和Jeff Bowman。我尝试了两个答案。我喜欢robertklep版本,因为它很短。在Jeff Bowman版本之后,我理解了异步执行中的许多潜在问题。

    UPD 4。我将我最初的承诺解决方案与robertklep版本混合添加一些糖。

    var R = require('ramda');
    
    // getAlpha returns alpha, etc.
    
    var promiseObj = function(obj, res){
        var promises = R.transpose(R.toPairs(obj));
        return new Promise(function(resolve, reject){
            Promise.all(promises[1]).then(function(results){
                res = res || {};
                resolve(R.merge(res, R.zipObj(promises[0], results)));
            });
        });
    };
    
    promiseObj({
        'alpha' : getAlpha(),
        'beta' : getBeta()
    }).then(function(res){ // res: { alpha: 'alpha', beta: 'beta' }
        return promiseObj({
            'epsilon' : getEpsilon(res.beta), 
        }, res);
    }).then(function(res){
        console.log(res); // res: { alpha: 'alpha', beta: 'beta', epsilon: 'beta_epsilon' }
    });
    

    P.S。 How do I access previous promise results in a .then() chain?也回答了我的问题,但是更通用。

2 个答案:

答案 0 :(得分:2)

非常优雅:

Promise.all([
  getAlpha(),
  getOmega(),
  getBeta().then((beta) => {
    return Promise.all([ beta, getEpsilon(beta), getDelta(beta) ]);
  })
]).then((results) => {
  let alpha   = results[0];
  let omega   = results[1];
  let beta    = results[2][0];
  let epsilon = results[2][1];
  let delta   = results[2][2];
  ...
});

答案 1 :(得分:1)

(此处假设getAlphagetBetagetOmegagetEpsilongetDelta均返回承诺。)

按顺序:

function getInSequence() {
  // Get alpha immediately.
  let alphaPromise = getAlpha();
  // After alpha returns, get beta.
  let betaPromise = alphaPromise.then(() => getBeta());
  // After beta returns, get omega.
  let omegaPromise = betaPromise.then(() => getOmega());
  // After omega returns, use then to get a value from beta (which has resolved
  // by now), which we feed into getEpsilon.
  let epsilonPromise = omegaPromise.then(() => betaPromise)
                                   .then(beta => getEpsilon(beta));
  // After epsilon returns, use then to get that beta value to pass along.
  let deltaPromise = epsilonPromise.then(() => betaPromise)
                                   .then(beta => getDelta(beta));
  // Return a promise that resolves into an array of everything.
  return Promise.all([alphaPromise, betaPromise, omegaPromise, 
                      epsilonPromise, deltaPromise])
                .catch(err => handleErr(err));
}

同时:

function getInSequence() {
  // Get alpha immediately.
  let alphaPromise = getAlpha();
  // Get beta immediately.
  let betaPromise = getBeta();
  // Get omega immediately.
  let omegaPromise = getOmega();
  // Get epsilon as soon as beta returns.
  let epsilonPromise = betaPromise.then(beta => getEpsilon(beta));
  // Get delta as soon as beta returns.
  let deltaPromise = betaPromise.then(beta => getDelta(beta));
  // Return a promise that resolves into an array of everything.
  return Promise.all([alphaPromise, betaPromise, omegaPromise,
                      epsilonPromise, deltaPromise])
                .catch(err => handleErr(err));
}