【问题标题】:How to return data from the done() callback when working with Airtable.js?使用 Airtable.js 时如何从 done() 回调中返回数据?
【发布时间】:2018-10-11 01:08:54
【问题描述】:

首先,让我从我想要实现的目标开始。我想查询 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() 部分中注释了一些行,因为据我在处理此问题时了解到,该代码未按我预期的顺序执行。

我怎样才能foreachaccumulator.find() 获得我需要的记录?

【问题讨论】:

    标签: javascript airtable


    【解决方案1】:

    看起来您遇到的问题是 done 回调在您的 forEach 中的 find 调用完成之前执行。 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
      });
    });
    

    【讨论】:

    • 感谢您的评论,但不幸的是,我不再从事此项目。希望当我做同样的事情并遇到同样的问题时,它会再次帮助其他人或我。 ;)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-09-20
    • 2019-11-23
    • 1970-01-01
    • 1970-01-01
    • 2016-06-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多