2016-06-15 2 views
1

У меня есть приложение для асинхронного javascript для браузера, и я хочу использовать обещания. Каждый асинхронный вызов имеет тот же шаблон, который может быть преобразован в обещание. Моя проблема в том, что у меня есть зависимости данных в цепочке обратного вызова. Некоторые упрощенный код:Преобразование цепи обратного вызова с зависимостями данных с обещаниями

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 зависят от бета-значения.

  2. Мне нужны все собранные данные в последнем обратном вызове с самого первого вызова и другого.

Я смотрю здесь 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 является HTTP-запросом GET, поэтому эти вызовы могут работать параллельно.

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? дает ответ на мой вопрос тоже, но более общим образом.

+0

Вы можете передать объект по цепочке, а затем уничтожить объект в каждой функции обещания.Если вы хотите, чтобы они были независимыми, вы можете передать массив, затем уничтожить его, выполнить функцию, вставить новое значение в массив и передать его в следующее обещание. – Yerken

+1

Если вам просто нужно передать значение предыдущего обещания другому, объект, который вы использовали, не нужен, вы можете вернуть новое обещание в предыдущем обратном вызове и связать следующее обещание. getAlpha.then (getBeta) .then (getDelta); –

+5

К сожалению, я хотел закрыть как дубликат [Как получить доступ к предыдущим результатам в цепочке .then()?] (Http://stackoverflow.com/q/28250680/1048572) – Bergi

ответ

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

Примечательно, что это делает все три (альфа, омега и бета) встречаются параллельно, а не в последовательности, что может быть не плохо, но не является ни одним из вариантов в вопросе. –

+0

@JeffBowman благодарит за это! Я предположил из вопроса, что параллелизм не будет проблемой, но лучше быть откровенным в этом :-) – robertklep

1

(предположим, что каждый из getAlpha, getBeta, getOmega, getEpsilon и getDelta возвращается обещание.)

В последовательности:

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)); 
} 
Смежные вопросы