Как вернуть данные из колбэка done() при работе с Airtable.js?

Во-первых, позвольте мне начать с того, чего я хочу достичь. Я хочу запросить таблицу в Airtable, которая включает в себя данные из связанной таблицы. Airtable возвращает идентификаторы записей в связанных таблицах, поэтому мне нужно сделать второй поиск по каждой из этих записей, чтобы получить нужные мне данные (например, Name). В конечном счете, я хочу вернуть клиенту идентификатор и другие поля из связанной записи. Проблема в том, что мне не удается этого добиться из-за асинхронной природы 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() записи мне нужны?

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
  });
});
Другие вопросы по тегам