[英]The use of async await promises for API calls via cypress
[英]How to use promises to wait for async API calls
我正在創建一個API,當執行GET時,將對News API進行一系列調用,將新聞文章標題提取為一個巨大的字符串,然后將該字符串處理為一個對象,以交付給前端的wordcloud。 到目前為止,我已經能夠使用下划線的_.after
和request-promise來使我的應用程序等待所有API調用完成,然后再調用processWordBank()
,該方法將巨型字符串並清理為對象。 但是,一旦processWordBank()
,我將不知道程序的流向何處。 理想情況下, processWordBank()
將obj返回到路由器中的cloudObj,以便可以將obj傳遞到res.json()
並吐出作為響應。 我相信我對_.after
使用使我處於一種怪異的境地,但這是我能夠獲得異步調用以完成下一步操作之前的唯一方法。 有什么建議么?
(我已嘗試省略所有不必要的代碼,但請告知是否足夠)
// 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
}
異步流程中的一個大問題是,您使用共享變量sourceString
來處理結果。 當您多次調用getNewsApi()
您的結果將不可預測,並且將始終不相同,因為沒有異步調用執行的預定義順序。 不僅如此,而且您永遠也不會重置它,因此所有后續調用也將包括先前調用的結果。 避免在異步調用中修改共享變量,而直接使用結果。
我已經能夠使用下划線的
_.after
和request-promise使我的應用程序等待所有API調用完成之后再調用processWordBank()
盡管可以使用_.after
,但可以通過promise很好地完成此操作,並且由於您已經在請求中使用了promise,因此僅從它們中收集結果即可。 因此,因為您要等到所有API調用完成后才可以使用Promise.all ,因此一旦所有承諾都實現后,它會返回一個承諾,該承諾將使用所有承諾的值的數組進行解析。 讓我們看一個非常簡單的示例,看看Promise.all的工作原理:
// 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')
現在我們可以修改getNewsApi()
以使用Promise.all
。 給Promise.all
數組是您正在循環中執行的所有API請求。 這將使用Array.protoype.map創建。 而且,除了在_.pluck
返回的數組之外創建字符串_.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()
返回的getNewsApi()
滿足了所有請求的數組,這些請求本身返回了一組文章。 那是一個2d數組,但是大概您想要一個1d數組,其中包含您的processWordBank()
函數的所有文章,因此我們可以首先對其進行展平。
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功能進行清理(而無需上面片段的注釋):
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)
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.