You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Google API时出现Cannot destructure property 'data'错误的解决问询

解决Gmail API未处理Promise拒绝警告与程序挂起问题

我帮你拆解下问题根源,再给出针对性的修复方案:

问题出在哪?

1. API回调的解构错误

你在gmail.users.labels.list的回调里直接写了(err, { data }),但当API返回错误时,第二个参数是undefined——这时候去解构data就会触发Cannot destructure property 'data' of 'undefined'的错误。Google API的回调内部会把这种未捕获的错误转化为Promise拒绝,也就是你看到的警告。

2. 回调参数没传对

你的listLabels函数需要两个参数:auth和callback,但看调用链:test(listLabels) → authorize(..., methodname) → callback(oAuth2Client)——这里的callback就是listLabels,但只传了auth,导致listLabels里的callback是undefined。当你调用callback()时,本质是调用一个不存在的函数,这又会引发新的错误,加重了Promise拒绝的问题。

3. Async流程没衔接好

async.series里的getStuff函数调用了异步的test,但没等test执行完就去判断interimlabels,而且没在异步操作完成后调用async的回调。这就导致async以为任务还没完成,程序就挂起了;而之前的listLabels里调用callback()(虽然是undefined),反而误打误撞推进了流程,但带来了警告。

修复方案

我把你的代码做了针对性修改,关键改动点都标了注释:

var async = require('async-kit');
const fs = require('fs');
const readline = require('readline');
const { google } = require('googleapis');

const SCOPES = ['https://www.googleapis.com/auth/gmail.readonly'];
const TOKEN_PATH = 'credentials.json';

// 修改test函数,新增callback参数,用于衔接async流程
function test(methodname, callback) {
  console.log('called');
  fs.readFile('client_secret.json', (err, content) => {
    if (err) {
      console.log('Error loading client secret file:', err);
      return callback(err); // 将错误传递给async
    }
    // 把async的callback传递给authorize,最终传给listLabels
    authorize(JSON.parse(content), (auth) => methodname(auth, callback));
  });
}

/**
 * Create an OAuth2 client with the given credentials, and then execute the
 * given callback function.
 * @param {Object} credentials The authorization client credentials.
 * @param {function} callback The callback to call with the authorized client.
 */
function authorize(credentials, callback) {
  console.log('authorized');
  const { client_secret, client_id, redirect_uris } = credentials.installed;
  const oAuth2Client = new google.auth.OAuth2(
    client_id,
    client_secret,
    redirect_uris[0]
  );

  // Check if we have previously stored a token.
  fs.readFile(TOKEN_PATH, (err, token) => {
    if (err) return getNewToken(oAuth2Client, callback);
    oAuth2Client.setCredentials(JSON.parse(token));
    callback(oAuth2Client);
  });
}

/**
 * Get and store new token after prompting for user authorization, and then
 * execute the given callback with the authorized OAuth2 client.
 * @param {google.auth.OAuth2} oAuth2Client The OAuth2 client to get token for.
 * @param {getEventsCallback} callback The callback for the authorized client.
 */
function getNewToken(oAuth2Client, callback) {
  const authUrl = oAuth2Client.generateAuthUrl({
    access_type: 'offline',
    scope: SCOPES,
  });
  console.log('Authorize this app by visiting this url:', authUrl);
  const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout,
  });
  rl.question('Enter the code from that page here: ', (code) => {
    rl.close();
    oAuth2Client.getToken(code, (err, token) => {
      if (err) return callback(err);
      oAuth2Client.setCredentials(token);
      // Store the token to disk for later program executions
      fs.writeFile(TOKEN_PATH, JSON.stringify(token), (err) => {
        if (err) return console.error(err);
        console.log('Token stored to', TOKEN_PATH);
      });
      callback(oAuth2Client);
    });
  });
}

/**
 * Lists the labels in the user's account.
 *
 * @param {google.auth.OAuth2} auth An authorized OAuth2 client.
 * @param {function} callback Async流程的回调函数
 */
function listLabels(auth, callback) {
  const gmail = google.gmail({ version: 'v1', auth });
  // 先判断err,再处理data,避免解构undefined
  gmail.users.labels.list({ userId: 'me' }, (err, response) => {
    if (err) {
      console.log('The API returned an error: ' + err);
      return callback(err); // 把错误传给async
    }
    // 现在response肯定存在,再解构data
    const { data } = response;
    const labels = data.labels;
    console.log('got labels');
    if (labels.length) {
      interimlabels = labels;
      labels.forEach((label) => {
        console.log(`- ${label.name}`);
      });
    } else {
      console.log('No labels found.');
    }
    callback(null, labels); // 通知async任务完成,传递结果
  });
}

var interimlabels = [];

async.series([
  function getStuff(callback) {
    // 把async的callback传给test,最终传到listLabels
    test(listLabels, callback);
    // 移除原来的同步判断,因为listLabels是异步的,这里判断没用
  }
])
.exec(function(error, results) {
  if (error) {
    console.log('Error occurred:', error);
  } else {
    console.log('done');
    console.log('labels', results[0]); // results里存的是listLabels返回的labels
  }
});

关键改动说明:

  1. 修复API回调:把(err, { data })改成(err, response),先判断err,再从存在的response中解构data,彻底避免undefined解构错误。
  2. 传递正确的回调:修改test函数接收callback参数,然后通过authorize把这个callback传递给listLabels,确保listLabels里的callback是async.series的任务回调,用来推进流程。
  3. 衔接Async流程:getStuff里直接调用test(listLabels, callback),把async的回调传给test,让listLabels完成后调用这个回调,通知async任务结束,解决程序挂起问题。
  4. 错误处理优化:所有异步操作的错误都通过回调传递给async.series的最终处理函数,便于统一排查问题。

这样修改后,程序既能正确输出邮箱标签,又不会有未处理Promise拒绝警告,也不会挂起。

内容的提问来源于stack exchange,提问作者jhonathan myers

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 07:41:42