2013-10-15 153 views
3

我遇到了什麼似乎是節點JS初學者和異步請求的傳統問題。NodeJS與異步請求

我有未知數量的用戶生成的URL,隨後存儲在我的Node JS服務器上的數組中。 Node JS服務器必須遍歷這些URL,依次向每個請求。它必須按順序執行,並且必須等待每個響應,然後才能轉到下一個URL(當發出新請求時)。最終的結果應該是所有響應(恰好是JSON)的有序集合,很好地作爲JSON對象一起存儲起來,而這些JSON對象又可以在準備就緒時發送回客戶端。

我想我應該使用async NodeJS庫,並且我已經使用needle來提出請求。

URLs = ["http://a", "http://s", "http://d"]; 
async.eachSeries(URLs, function (URL, callback) { ..... }); 

我不清楚如何使用異步以確保針請求已完成,並移動到下一個URL請求之前相應的存儲響應。以下是我的Needle請求的示例。

needle.get(URL, options, function(error, response, body){ ... }); 

對整個問題的部分或完整解決方案是受歡迎的。

+0

我在這裏使用了遞歸方法:http://stackoverflow.com/a/6048595/2063246(完全適合我) – Halogen

+0

我會在可能的情況下推薦承諾 – jtromans

回答

4

這裏有兩個例子,一個是節省了一個與async.eachSeries和一個收集與async.mapSeries所有結果的結果之一,然後將它們保存一次全部

URLs = ["http://a", "http://s", "http://d"]; 
function iterator1(URL, done){ 
    var options = {}; 
    needle.get(URL, options, function(error, response, body){ 
    if(error){ return done(error) }; 
    processAndSaveInDB(body, function(err){ 
     if(err){ return done(err) }; 
     done(null); 
    }); 
    }); 
}; 

async.eachSeries(URLs 
, iterator1 
, function (err){ 
    // global callback for async.eachSeries 
    if(err){ 
    console.log(err) 
    } else { 
    console.log('All Needle requests successful and saved'); 
    } 
}); 

// Here is a similar technique using async.map, it may be more suitable 
function iterator2(URL, done){ 
    var options = {}; 
    needle.get(URL, options, function(error, response, body){ 
    if(error){ return done(error) }; 
    done(null, body); 
    }); 
}; 

async.mapSeries(URLs 
, iterator2 
, function (err, results){ 
    // global callback for async.mapSeries 
    if(err){ 
    console.log(err) 
    } else { 
    console.log('All Needle requests successful'); 
    // results is a 1 to 1 mapping in order of URLs > needle.body 
    processAndSaveAllInDB(results, function(err){ 
     if(err){ return done(err) }; 
     console.log('All Needle requests saved'); 
     done(null); 
    }); 
    } 
}); 

我不清楚如何使用async來確保Needle請求已完成,並在轉到下一個URL請求之前相應地存儲該響應。

series異步函數的變體負責這個;您只需確保不要調用迭代器函數的done回調,直到準備好繼續。在實踐中,這意味着將調用done在你內心深處的回調(如你的針回調)

+0

在重讀您的問題時,mapSeries解決方案可能更多有用;你可以用'concatenateJSONResults'(它可能是同步的)替換我的dummy'processAndSaveAllInDB',然後發送你的迴應 – Plato

+0

嘗試兩種方法,並且會去mapSeries選項。非常感謝你。當我使用相互函數來實現相似類型的結果時,我有一些其他範例,所以這種方法將會非常有用。 – jtromans

10

有了承諾,你可以做到這一點與:

var Promise = require("bluebird"); 
var get = Promise.promisify(needle.get, needle); 

var URLs = ["http://a", "http://s", "http://d"]; 
var current = Promise.fulfilled(); 
Promise.map(URLs, function (URL) { 
    current = current.then(function() { 
     return get(URL); 
    }); 
    return current; 
}).map(function(responseAndBody){ 
    return JSON.parse(responseAndBody[1]); 
}).then(function (results) { 
    console.log(results); 
}).catch(function (e) { 
    console.error(e); 
}); 

作爲獎勵,你的服務器不會崩潰的時候網站有無效的JSON或與錯誤消息/空身體響應。當用手寫字時,您需要手動嘗試捕捉,但承諾處理catch()中的這兩種錯誤。由於這些網址是由用戶提供的,因此如果您不向非承諾代碼添加手動嘗試抓取,他們可以輕鬆地拒絕服務器。

+0

感謝您提供此解決方案。我會放棄這一點。我將柏拉圖的回答標記爲「正確」,因爲它堅持我在問題中提到的圖書館。無論他們是否是這項工作的最佳工具,肯定都有爭議。如果我能將兩者都標記爲正確的話,我會的。 – jtromans

+0

@ jtromans當然。順便說一句,如果你不能達到上述目的,請讓我知道。 – Esailija

+0

@Esailija你設置了'var current = Promise.fulfilled()'並使用它'Promise.map'的任何原因?我們可以在第一個map函數中返回get(URL)? – abhilash