使用Promise调用DynamoDB documentClient.get()时部分哈希键查询失败
DynamoDB documentClient.get() 混合回调与Promise时部分哈希键查询失败问题分析
问题背景
使用AWS SDK的documentClient.get()查询DynamoDB时,出现查询结果不一致的情况:
- 手动在AWS控制台创建的条目(db1001、db1013)可查询成功
- 通过AWS SDK写入的条目(db1002至db1012)查询失败
- 同时传入回调函数并调用
.promise()时触发错误,移除.promise()仅用回调、或仅用.promise()时所有查询均正常
原始代码
let fetchOneByKey = async (collection_name, entry_id) => { console.log('fetching table:', collection_name, "using key:", entry_id); var params = { TableName: collection_name || 'Lifestyle', Key: {"entry_id": entry_id} }; let payload = await docClient.get(params,(err, data)=>{ if(err){ console.log('err::',err) } else { console.log('data::',data) } }).promise(); return payload; }
报错信息
fetching table: lifestyle-dictionary using key: db1002 err:: Error [TypeError]: Cannot read properties of undefined (reading 'push') at Request.HTTP_DATA (/Users/gazakorompis/snow-react/app-lifestyle/dynamodb/node_modules/aws-sdk/lib/event_listeners.js:499:35) at Request.callListeners (/Users/gazakorompis/snow-react/app-lifestyle/dynamodb/node_modules/aws-sdk/lib/sequential_executor.js:106:20) at Request.emit (/Users/gazakorompis/snow-react/app-lifestyle/dynamodb/node_modules/aws-sdk/lib/sequential_executor.js:78:10) at Request.emit (/Users/gazakorompis/snow-react/app-lifestyle/dynamodb/node_modules/aws-sdk/lib/request.js:686:14) at IncomingMessage.onReadable (/Users/gazakorompis/snow-react/app-lifestyle/dynamodb/node_modules/aws-sdk/lib/event_listeners.js:399:32) at IncomingMessage.emit (node:events:513:28) at IncomingMessage.emit (node:domain:489:12) at emitReadable_ (node:internal/streams/readable:590:12) at onEofChunk (node:internal/streams/readable:568:5) at readableAddChunk (node:internal/streams/readable:275:5) { code: 'TypeError', time: 2023-02-07T07:41:18.876Z, statusCode: 200, retryable: false, retryDelay: 31.881162055453217 } node:internal/process/promises:288 triggerUncaughtException(err, true /* fromPromise */); ^ Error [TypeError]: Cannot read properties of undefined (reading 'push') at Request.HTTP_DATA (/Users/gazakorompis/snow-react/app-lifestyle/dynamodb/node_modules/aws-sdk/lib/event_listeners.js:499:35) at Request.callListeners (/Users/gazakorompis/snow-react/app-lifestyle/dynamodb/node_modules/aws-sdk/lib/sequential_executor.js:106:20) at Request.emit (/Users/gazakorompis/snow-react/app-lifestyle/dynamodb/node_modules/aws-sdk/lib/sequential_executor.js:78:10) at Request.emit (/Users/gazakorompis/snow-react/app-lifestyle/dynamodb/node_modules/aws-sdk/lib/request.js:686:14) at IncomingMessage.onReadable (/Users/gazakorompis/snow-react/app-lifestyle/dynamodb/node_modules/aws-sdk/lib/event_listeners.js:399:32) at IncomingMessage.emit (node:events:513:28) at IncomingMessage.emit (node:domain:489:12) at emitReadable_ (node:internal/streams/readable:590:12) at onEofChunk (node:internal/streams/readable:568:5) at readableAddChunk (node:internal/streams/readable:275:5) { code: 'TypeError', time: 2023-02-07T07:41:18.876Z, statusCode: 200, retryable: false, retryDelay: 31.881162055453217 } Node.js v18.7.0
关键现象总结
- 手动创建的条目查询成功,SDK写入的条目查询失败:手动创建的数据结构简单,响应处理时未触发内部逻辑冲突;SDK写入的数据可能包含更多字段或不同序列化格式,触发了错误
- 同时使用回调和
.promise()时出现错误,单独使用其中一种方式则完全正常
原因分析
AWS SDK v2的设计规则是不允许同时为请求传入回调函数并调用.promise()。这两种模式会初始化两套独立的响应处理逻辑,导致内部事件监听和数据收集状态混乱:
- 回调模式会注册
HTTP_DATA事件监听来收集响应数据 - Promise模式也会注册自己的事件处理逻辑
两者竞争处理响应流时,会导致某个用于存储数据的数组未被正确初始化,进而抛出Cannot read properties of undefined (reading 'push')的类型错误。
解决方案
方案1:仅使用Promise模式(推荐)
移除回调函数,仅通过.promise()配合async/await处理异步逻辑,这是现代Node.js代码的最佳实践:
let fetchOneByKey = async (collection_name, entry_id) => { console.log('fetching table:', collection_name, "using key:", entry_id); var params = { TableName: collection_name || 'Lifestyle', Key: {"entry_id": entry_id} }; try { const payload = await docClient.get(params).promise(); console.log('data::', payload); return payload; } catch (err) { console.log('err::', err); throw err; // 根据业务需求选择是否抛出错误 } }
方案2:仅使用回调模式
如果需要保留回调写法,不要调用.promise():
let fetchOneByKey = (collection_name, entry_id, callback) => { console.log('fetching table:', collection_name, "using key:", entry_id); var params = { TableName: collection_name || 'Lifestyle', Key: {"entry_id": entry_id} }; docClient.get(params, (err, data) => { if (err) { console.log('err::', err); callback(err); } else { console.log('data::', data); callback(null, data); } }); }
内容的提问来源于stack exchange,提问作者gfek
相关产品推荐
相关产品推荐

