使用Airtable.js时如何从done()回调中返回数据?

时间:2018-04-30 22:18:04

标签: javascript airtable

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

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

这是我的代码(仅供参考,这是在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()部分中评论了一些行,因为正如我在研究这个问题时所了解的那样,该代码并没有按照我预期的顺序执行。< / p>

我如何foreach通过accumulator.find()我需要的记录?

1 个答案:

答案 0 :(得分:0)

您似乎遇到的问题是,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
  });
});