1

首先,让我从我想要实现的目标开始。我想查询 Airtable 中的一个表,其中包含来自链接表的数据。Airtable 返回链接表中记录的 ID,因此我需要对每条记录进行第二次查找以获取我想要的数据(例如Name)。最终,我想将链接记录中的 ID其他字段返回给客户端。问题是,由于 API 的异步性质和我缺乏理解,我未能实现这一点。

到目前为止,我能做的最好的事情就是在done()他们的 API 回调中执行一些代码。问题是这是不可扩展的,因为这意味着我会让自己陷入回调地狱。(至少,这是我认为到目前为止我能够确定的。)

这是我的代码(仅供参考,这是要在 Azure 函数中运行的代码):

var Airtable = require("airtable");
var airtableApiKey = process.env.airtableApiKey;

module.exports = function (context, req) {
  context.log('JavaScript HTTP trigger function processed a request.');

  var base = new Airtable({
    apiKey: airtableApiKey
  }).base('appBASEID');
  var resultObj;
  var accumulator = [];

  base('Products').select({
    sort: [{
      field: 'Identifier',
      direction: 'asc'
    }]
  }).eachPage(function page(records, fetchNextPage) {
    records.forEach(function (record) {
      context.log('Retrieved ', record.get('Identifier'));
      context.log('Retrieved ', record.get('Vendor'));

      accumulator.push(record._rawJson);

      // base('Vendors').find(record.get('Vendor')[0], function( err, record) {
      //   if (err) { context.error(err); return; }
      //   context.log(record.get('Name'));
      // });
    });
    fetchNextPage();
  }, function done(error) {

    context.res = {
      // status: 200, /* Defaults to 200 */
      body: JSON.parse(JSON.stringify(accumulator))
    };

    context.done();
  });
};

您可以看到我在该.eachPage()部分中评论了一些行,因为正如我在处理此问题时所了解到的那样,该代码没有按照我预期的顺序执行。

我怎样才能foreach通过accumulator.find()我需要的记录?

4

1 回答 1

1

看起来您遇到的问题是done回调find在您的调用forEach完成之前执行。 forEach可能会阻塞,但每个find呼叫都不会。因此,在您成功提取所有链接记录之前,您继续获取更多页面,并最终获取所有页面。

这是我为管理它而编写的代码。请注意,它很长,可能还有改进的余地。我还尝试处理您的链接字段可能有多个元素以及您可能有多个您有兴趣查找的链接字段列的情况。

var base = Airtable.base(base_id)
var table = base.table(table_name);

// create a map between the linked record columns in your table
// and the table that those linked record's point to
var linked_fields = {
  'Local Column A': 'Foreign Table X',
  'Local Column B': 'Foreign Table Y'
}

// manage all records and promises
var all_records = [];
var all_promises = [];

// cycle through all pages in our table
table.select().eachPage(function page(records, fetchNextPage) {
  // for each record, go through each linked field and pull all associated linked records
  var page = records.map((record) => {
    // for each column, we want to check if the given record has any linked records
    // if it does, then go ahead and perform a fetch in the foreign table 
    // linked record fields are a list because there can be multiple linked records
    // so we have to loop through each ID
    var record_promises = Object.keys(linked_fields).map((field) => {
      if (record.fields[field] !== undefined) {
        let t = base.table(linked_fields[field]);
        var linked_promises = record.fields[field].map((foreign_record_id) => {
          return t.find(foreign_record_id);
        });

        // wait for this record to get all of its linked fields
        return Promise.all(linked_promises).then((values) => {
          // for each linked field, we don't need all the extra Airtable SDK noise
          // so just use the rawJSON structure from the foreign record
          // but update both the rawJson and fields structures in the local record
          values = values.map((v) => {
            return v._rawJson;
          });
          record.fields[field] = values;
          record._rawJson.fields[field] = values;
        });
      }
    });

    // wait for the record to finish updating all linked fields
    // and then return the record
    return Promise.all(record_promises).then(() => {
      return record;
    });
  });

  // we have to wait for all records in this page to get updated information
  // we can use all_promises to track all of our active promises
  all_promises.push(Promise.all(page));

  // we don't need to wait for everything to resolve before fetching the next page
  // and we probably don't want to wait.  
  // Airtable pagination will die if you wait too long in between calls 
  // and you have to start all over
  fetchNextPage();

}, function done(error) {
  if (error) {
    reject(error);
  }

  // once we've fetched all pages, wait for all those pages to settle
  // we will get a list of lists at the end of this, where each page is a different list
  // so we can now flatten it into a single list by pushing all records into all_records
  Promise.all(all_promises).then((results) => {
    for (var i in results) {
      all_records.push.apply(all_records, results[i]);
    }
    context.res = {
      // status: 200, /* Defaults to 200 */
      body: JSON.parse(JSON.stringify(all_records))
    };

    context.done();
  }).catch((err) => {
    // handle error response
  });
});
于 2018-12-10T22:39:15.197 回答