嵌套数千个Promise场景下的async/await实现是否正确?
核心结论
你担心的「内层getPriceChange的await会导致外层循环串行执行」的假设不成立,当前代码的所有getPriceChange调用本身是并行触发的。
原理说明
外层getPriceChanges的循环逻辑里,只是直接调用getPriceChange并将返回的Promise实例推入changes数组,全程没有在循环体内使用await,循环不会等待任意一个getPriceChange的异步逻辑执行完成就会继续处理下一个ticker,1500次异步请求会在极短时间内全部触发,之后才会通过await Promise.all(changes)统一等待所有请求返回结果,所以不存在串行执行的问题。
当前Lambda 30秒耗时的原因及优化方案
当前执行耗时较长的核心原因是1500次独立的DynamoDB单Key查询带来的大量网络IO开销和请求排队开销,可通过以下方案优化:
- 改用DynamoDB的
BatchGetItem接口批量查询数据,单次批量请求最多支持同时查询100条数据,1500次查询只需15次请求即可完成,可将查询耗时降低80%以上,同时进一步降低读请求成本。 - 若受查询逻辑限制无法使用批量查询,可增加并发数控制,限制同时发起的DynamoDB请求数在50~100区间,避免一次性发起过多请求触发DynamoDB读吞吐量限流导致的重试耗时。
- 对重复查询的热点数据增加缓存层,可使用Lambda内存级缓存或ElastiCache缓存高频查询的价格数据,避免重复查询DynamoDB。
外层getPriceChanges代码
async function getPriceChanges (since, breakpoint) { const changes = [] for (const [i, ticker] of tickers.entries()) { if (!ticker.includes('BIDR') && !ticker.includes('BIFI')) { changes.push(getPriceChange(ticker, since, breakpoint)) } } const res = await Promise.all(changes) let symbol let maxChange = 0 let slope = 0 for (const ticker of res) { if (ticker.percentChange > maxChange && ticker.slope > Math.pow(10, -1 * NUM_DECIMALS)) { maxChange = ticker.percentChange symbol = ticker.symbol slope = ticker.slope } } return { symbol, maxChange: Math.round(100 * maxChange) / 100, slope: round(slope, NUM_DECIMALS) } }
内层getPriceChange代码
async function getPriceChange (symbol, since, breakpoint) { const params = { TableName: DYNAMODB_TABLE, ProjectionExpression: '#timestamp, price', KeyConditionExpression: 'symbol = :symbol and #timestamp > :timestamp', ExpressionAttributeNames: { '#timestamp': 'timestamp' }, ExpressionAttributeValues: { ':symbol': symbol, ':timestamp': since } } const res = await documentClient.query(params).promise() const prev = [] const recent = [] const trendData = [] for (const data of res.Items) { if (data.timestamp < breakpoint) { prev.push(data.price) } else { trendData.push({ x: trendData.length, y: data.price }) recent.push(data.price) } } let sumPrev = 0 let sumRecent = 0 for (const price of prev) { sumPrev += price } for (const price of recent) { sumRecent += price } const avgPrev = sumPrev / prev.length const avgRecent = sumRecent / recent.length const trend = createTrend(trendData, 'x', 'y') return { symbol, percentChange: (100 * (avgRecent - avgPrev)) / avgRecent, slope: trend.slope } }
内容的提问来源于stack exchange,提问作者gvasquez
相关产品推荐
相关产品推荐

