2017-02-20 9 views
0

私は、News APIへの一連の呼び出しが行われ、ニュース記事のタイトルが巨大な文字列に抽出され、その文字列がフロントエンドのワードクラウドに配信されるオブジェクト。これまでのところ、私はアンダースコアの_.afterを使用し、すべてのAPI呼び出しが完了するまで私のアプリを待たせることを約束しました。processWordBank()を呼び出す前に、巨大な文字列を取り除きオブジェクトにクリーンアップします。しかし、いったんprocessWordBank()が呼び出されると、プログラムの流れはどこか分かりません。理想的には、processWordBank()はobjをルータのcloudObjに返します。objをres.json()に渡して応答として吐き出すことができます。私は_.afterの私の使用が奇妙な状況に置かれていると信じていますが、次の希望のアクションに進む前に、非同期呼び出しを終了して終了する唯一の方法です。助言がありますか?非同期API呼び出しを待つための約束の使い方

(私はすべての不要なコードを残したが、これは不十分であるなら、私が知っているようにしようとしました)

// includes... 
var sourceString = "" 
// router 
export default ({ config }) => { 
    let news = Router() 
    news.get('/', function(req, res){ 
    var cloudObj = getSources() 
     res.json({ cloudObj }) 
    }) 
    return news 
} 

// create list of words (sourceString) by pulling news data from various sources 
function getSources() { 
    return getNewsApi() 

} 
// NEWS API 
// GET top 10 news article titles from News API (news sources are determined by the values of newsApiSource array) 
function getNewsApi() { 
    var finished = _.after(newsApiSource.length, processWordBank) 
    for(var i = 0; i < newsApiSource.length; i++) { 
    let options = { 
     uri: 'https://newsapi.org/v1/articles?source=' + newsApiSource[i] + '&sortBy=' + rank + '&apiKey=' + apiKey, 
     json: true 
    } 
    rp(options) 
    .then(function (res) { 
     let articles = res.articles // grab article objects from the response 
     let articleTitles = " " + _.pluck(articles, 'title') // extract title of each news article 
     sourceString += " " + articleTitles // add all titles to the word bank 
     finished() // this async task has finished 
    }) 
    .catch(function (err) { 
     console.log(err) 
    }) 
    } 
} 

// analyse word bank for patterns/trends 
function processWordBank(){ 
    var sourceArray = refineSource(sourceString) 
    sourceArray = combineCommon(sourceArray) 
    sourceArray = getWordFreq(sourceArray) 
    var obj = sortToObject(sourceArray[0], sourceArray[1]) 
    console.log(obj) 
    return obj 
} 
+0

'' *実際にしてみてください)(終了の代わりに*何かを返す**おそらく 'return finished()' - しかし、あなたは 'getNewsApi'が何かを返すことを望みます。 –

答えて

1

あなたの非同期の流れに大きな問題は、あなたが結果を処理するために、共有変数sourceStringを使用することです。 getNewsApi()への複数の呼び出しがある場合、結果は予測できず、非同期呼び出しが実行される事前定義された順序がないため、常に同じではありません。それだけでなく、それをリセットすることはありません。したがって、それ以降のすべてのコールには、以前のコールの結果も含まれます。非同期呼び出しで共有変数を変更しないで、結果を直接使用することは避けてください。私は、アンダースコアの_.afterと私のアプリは、すべてのAPI呼び出しは、それが_.afterを使用することが可能であろうがprocessWordBank()

を呼び出す前に完了してまで、これをすることができ待たせるための要求、約束を使用することができました

約束どおりにうまくやっており、あなたの要求にすでに約束をしているので、結果を収集するだけです。したがって、すべてのAPI呼び出しが完了するまで待つ必要があるため、Promise.allを使用すると、すべての約束の値の配列で解決された約束事を返すことができます。今、私たちはPromise.allを使用するgetNewsApi()を変更することができます

// Promise.resolve() creates a promise that is fulfilled with the given value 
 
const p1 = Promise.resolve('a promise') 
 
// A promise that completes after 1 second 
 
const p2 = new Promise(resolve => setTimeout(() => resolve('after 1 second'), 1000)) 
 
const p3 = Promise.resolve('hello').then(s => s + ' world') 
 
const promises = [p1, p2, p3] 
 

 
console.log('Waiting for all promises') 
 
Promise.all(promises).then(results => console.log('All promises finished', results)) 
 
console.log('Promise.all does not block execution')

:さんはどのようにPromise.all作品を見るために非常に簡単な例を見てみましょう。 Promise.allに与えられた約束事は、あなたのループでやっているすべてのAPIリクエストです。これはArray.protoype.mapで作成されます。また、_.pluckから返された配列から文字列を作成する代わりに、配列を直接使用するだけで済みます。そのため、文字列を末尾の配列に解析する必要はありません。

function getNewsApi() { 
    // Each element is a request promise 
    const apiCalls = newsApiSource.map(function (source) { 
    let options = { 
     uri: 'https://newsapi.org/v1/articles?source=' + source + '&sortBy=' + rank + '&apiKey=' + apiKey, 
     json: true 
    } 
    return rp(options) 
     .then(function (res) { 
     let articles = res.articles 
     let articleTitles = _.pluck(articles, 'title') 
     // The promise is fulfilled with the articleTitles 
     return articleTitles 
     }) 
     .catch(function (err) { 
     console.log(err) 
     }) 
    }) 
    // Return the promise that is fulfilled with all request values 
    return Promise.all(apiCalls) 
} 

次に、ルータの値を使用する必要があります。私たちは、getNewsApi()から返された約束がすべての要求の配列を満たすことを知っています。これは2次元配列ですが、processWordBank()関数のすべての記事を含む1次元配列が必要になると思われるので、まずフラット化できます。

export default ({ config }) => { 
    let news = Router() 
    new.get('/', (req, res) => { 
    const cloudObj = getSources() 
    cloudObj.then(function (apiResponses) { 
     // Flatten the array 
     // From: [['source1article1', 'source1article2'], ['source2article1'], ...] 
     // To: ['source1article1', 'source1article2', 'source2article1', ...] 
     const articles = [].concat.apply([], apiResponses) 
     // Pass the articles as parameter 
     const processedArticles = processWordBank(articles) 
     // Respond with the processed object 
     res.json({ processedArticles }) 
    }) 
    }) 
} 

最後に、processWordBank()は、共有変数を使用する代わりに入力パラメータを使用するように変更する必要があります。refineSourceは、すでに配列を渡しているため(他の変更を加えない限り)、もはや必要ありません。ボーナスとして

function processWordBank(articles) { 
    let sourceArray = combineCommon(articles) 
    sourceArray = getWordFreq(sourceArray) 
    var obj = sortToObject(sourceArray[0], sourceArray[1]) 
    console.log(obj) 
    return obj 
} 

ルータとgetNewsApi()は、(上記のスニペットからのコメントなし)いくつかのES6 featuresを使用してクリーンアップすることができます。

export default ({ config }) => { 
    const news = Router() 
    new.get('/', (req, res) => { 
    getSources().then(apiResponses => { 
     const articles = [].concat(...apiResponses) 
     const processedArticles = processWordBank(articles) 
     res.json({ processedArticles }) 
    }) 
    }) 
} 

function getNewsApi() { 
    const apiCalls = newsApiSource.map(source => { 
    const options = { 
     uri: `https://newsapi.org/v1/articles?source=${source}&sortBy=${rank}&apiKey=${apiKey}`, 
     json: true 
    } 
    return rp(options) 
     .then(res => _.pluck(res.articles, 'title')) 
     .catch(err => console.log(err)) 
    }) 
    return Promise.all(apiCalls) 
} 
+0

これは非常に役に立ちました。 APIコールの約束を使用して私の頭の中を包み込むのを助けました。ありがとうございました! – wrobbinz

関連する問題