2017-01-17 8 views
0

Angularの非同期に問題があります。基本的に、私はいくつかのカードをループしています。特定の2種類のカードにはAPIコールが必要ですが、その種類のカードにはAPIコールは必要ありません。すべてのカードをループした後、完成したカードの配列が返されますが、私はAPI呼び出しを必要としないカードを返すだけです。 API呼び出しを必要とする代わりに、4つのすべてのなかった、しか返されるfinishedArrayは1枚のイエローカードが含まれています。この例ではAngular - forEachループでAPI呼び出しを待ってから配列を返す前に

// If color of colorCard is blue, it needs 2 API calls 
// If color of colorCard is red, it needs 1 API call 
// If color of colorCard is yellow, it doesn't need an API call 
// Pretend for this example that colorCards has one yellow card, one blue card, and two red cards 

var buildCards = function() { 
    var finishedArray = []; 
    var promises = []; 
    colorCards.forEach(function(e, i){ 
    if (e.color === 'blue') { 
     promises.push(firstBlueApiCall); 
     var firstBlueIdx = 0; 
     promises.push(secondBlueApiCall); 
     var secondBlueIdx = 1; 
    } else if (e.color === 'red') { 
     promises.push(redApiCall); 
     var redIdx = 0; 
    } 

    // If card is blue or red, complete API calls before pushing to finishedArray 
    if (promises.length > 0) { 
     $q.all(promises).then(function(response) { 
      if (e.color === 'blue') { 
      e.firstBlueData = response[firstBlueIdx]; 
      e.secondBlueData = response[secondBlueIdx]; 
      } else if (e.color === 'red') { 
      e.redData = response[redIdx]; 
      } 
      finishedArray.push(e); 
      promises = []; 
     }); 
    // If card is yellow, push to finishedArray without making any API calls 
    } else { 
     finishedArray.push(e); 
     promises = []; 
    } 
    }) 
    return finishedArray; 
} 

これは私がどのように働いているで作られた迅速なモックアップがありますカード。赤色/青色のカードがAPI呼び出しを完了するまで、どのように 'return finishedArray'を待機させることができますか?ここで

+0

JavaScriptはシングルスレッドです。関数が完了すると、利用可能なデータまたは*未定*で実行される*保留中の*約束だけを返すことができます。 – georgeawg

答えて

0

は、私がこれを解決することになった方法です:

var promiseFunction = function(card){ 
    var deferred = $q.defer(); 
    var localPromises = []; 

    if (card.color === 'blue') { 
    localPromises.push(blueApiCall1); var firstBlueIdx = promises.length - 1; 
    localPromises.push(blueApiCall2); var secondBlueIdx = promises.length - 1; 
    } else if (card.color === 'red') { 
    localPromises.push(redApiCall); var redIdx = promises.length - 1; 
    } 

    if (localPromises.length > 0) { 
     $q.all(promises).then(function(res) { 
     if (card.color === 'blue') { 
      card.firstBlueData = res[firstBlueIdx]; 
      card.secondBlueData = res[secondBlueIdx]; 
     } else if (card.color === 'red') { 
      card.redData = res[redIdx]; 
     } 
     deferred.resolve(card); 
     }); 
    } else { 
    deferred.resolve(card); 
    } 
    return deferred.promise; 
} 

var buildCards = function() { 
    var deferred = $q.defer(); 
    var finishedArray = []; 
    var promises = []; 
    colorCards.forEach(function(card, i){ 
    promises.push(promiseFunction(card)); 
    }); 
    $q.all(promises).then(function(finishedCards) { 
    deferred.resolve(finishedCards) 
    }) 
    return deferred.promise; 
} 

それは約束にダウンすべての方法だと、それがうまくいったので、私はそれを作ってみました。うまくいけば、これは将来同様の問題を持つ人々に役立ちます。

1

buildCards機能を簡素化することができる。

var buildCards = function(colorCards) { 
    //var deferred = $q.defer(); 
    //var finishedArray = []; 
    var promises = []; 
    colorCards.forEach(function(card, i){ 
    promises.push(promiseFunction(card)); 
    }); 
    //$q.all(promises).then(function(finishedCards) { 
    // deferred.resolve(finishedCards) 
    //}) 
    //return deferred.promise; 
    return $q.all(promises); 
} 

既に約束を返す$q.all方法として$q.deferと約束を製造する必要はありません。さらに、製造された約束は拒絶を適切に処理しません。 $q.allの約束が拒否された場合、$q.defer約束はハングアップして解決しません。

これはDeferred Anti-Patternとして知られており、避けるべきです。

同様に、promiseFunction機能はDeferred Anti-Patternを回避するように変更することができます。

var promiseFunction = function(card){ 
    //var deferred = $q.defer(); 
    var localPromises = []; 

    if (card.color === 'blue') { 
    localPromises.push(blueApiCall1); var firstBlueIdx = promises.length - 1; 
    localPromises.push(blueApiCall2); var secondBlueIdx = promises.length - 1; 
    } else if (card.color === 'red') { 
    localPromises.push(redApiCall); var redIdx = promises.length - 1; 
    } 

    var cardPromise; 
    if (localPromises.length > 0) { 
     //$q.all(promises).then(function(res) { 
     cardPromise = $q.all(localPromises).then(function(res) { 
     if (card.color === 'blue') { 
      card.firstBlueData = res[firstBlueIdx]; 
      card.secondBlueData = res[secondBlueIdx]; 
     } else if (card.color === 'red') { 
      card.redData = res[redIdx]; 
     } 
     //deferred.resolve(card); 
     //RETURN value to chain 
     return card; 
     }); 
    } else { 
     //deferred.resolve(card); 
     cardPromise = $q.when(card); 
    } 
    //return deferred.promise; 
    return cardPromise; 
} 

約束のthen方法は常にハンドラ関数に返された値に解決する新しい約束を返します。さらに、元の約定が却下された場合、成功ハンドラはスキップされ、拒否は新しい約束に連動して行われます。これにより、$q.deferの誤ったハングを回避できます。

$q.allで処理する約束がない場合、cardPromise$q.whenで作成できます。

約束の.thenメソッドを呼び出すと、新しい派生約束を返すので、約束のチェーンを作成することが容易に可能です。任意の長さのチェーンを作成することは可能であり、約束は別の約束(それ以上解決を延期する)で解決することができるので、チェーンの任意のポイントで約束を一時停止/延期することが可能です。 AngularJS $q Service API Reference - Chaining Promises

必ずチェーンの約束 - これにより、強力なAPI

を実現することができます。 Deferred Anti-Patternは使用しないでください。

関連する問題