Node.js中如何在回调完成后获取Google API联系人全量数据?
解决Node.js调用Google People API异步导致返回空数据的问题
问题根源
- 路由中
fs.readFile的回调函数未声明为async,内部的await authorize(...)无法生效,导致res.send(full_person_array)在数据获取前就执行。 authorize函数调用异步的listConnectionNames后立即resolve(),此时联系人数据还未写入全局数组。- 依赖全局变量
full_person_array存储数据,异步场景下无法保证数据已准备完成。
解决方案:全Promise/Async-Await重构
将所有回调式异步操作改为Promise风格,通过async/await严格控制执行顺序,避免全局变量依赖。
1. 重构路由函数
app.get('/contacts', async function(req, res) { try { // 用Promise版fs读取凭证文件 const content = await fs.promises.readFile('credentials.json'); const credentials = JSON.parse(content); // 等待授权完成,获取认证客户端 const authClient = await authorize(credentials); // 等待联系人数据获取完成 const contacts = await listConnectionNames(authClient); // 数据准备好后再响应 res.send(contacts); } catch (err) { console.error('获取联系人失败:', err); res.status(500).send('获取联系人失败,请检查日志'); } });
2. 修改authorize函数(移除回调,直接返回认证客户端)
function authorize(credentials) { return new Promise((resolve, reject) => { const { client_secret, client_id, redirect_uris } = credentials.installed; const oAuth2Client = new google.auth.OAuth2( client_id, client_secret, redirect_uris[0] ); // 用Promise版fs读取token文件 fs.promises.readFile(TOKEN_PATH) .then(token => { oAuth2Client.setCredentials(JSON.parse(token)); resolve(oAuth2Client); }) .catch(err => { // 无token时获取新token,需确保getNewToken返回Promise getNewToken(oAuth2Client) .then(() => resolve(oAuth2Client)) .catch(reject); }); }); } // 补充:将getNewToken改为Promise版本(适配原有逻辑) function getNewToken(oAuth2Client) { return new Promise((resolve, reject) => { const authUrl = oAuth2Client.generateAuthUrl({ access_type: 'offline', scope: ['https://www.googleapis.com/auth/contacts.readonly'] }); console.log('请访问以下链接授权:', authUrl); const rl = readline.createInterface({ input: process.stdin, output: process.stdout }); rl.question('输入授权页面的验证码: ', (code) => { rl.close(); oAuth2Client.getToken(code, (err, token) => { if (err) return reject('获取访问令牌失败:', err); oAuth2Client.setCredentials(token); // 保存token到文件 fs.promises.writeFile(TOKEN_PATH, JSON.stringify(token)) .then(() => resolve()) .catch(reject); }); }); }); }
3. 修改listConnectionNames(返回Promise,直接返回联系人数据)
function listConnectionNames(auth) { const service = google.people({ version: 'v1', auth }); return new Promise((resolve, reject) => { service.people.connections.list({ resourceName: 'people/me', pageSize: 50, personFields: 'names,emailAddresses,phoneNumbers', }, (err, res) => { if (err) return reject('API调用错误: ' + err); // 无联系人时返回空数组,避免undefined const connections = res.data.connections || []; console.log('获取到联系人数量:', connections.length); resolve(connections); }); }); }
关键改动说明
- 移除全局变量
full_person_array,改为函数内部返回数据,彻底避免异步竞态问题。 - 所有文件操作改用
fs.promises的Promise版本,配合await实现顺序执行。 authorize不再依赖回调传递客户端,直接返回Promise,让路由控制后续流程。- 用
try/catch统一捕获所有异步错误,返回友好的错误响应。
内容的提问来源于stack exchange,提问作者Impunkj
相关产品推荐
相关产品推荐

