Как вернуть данные из колбэка 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
});
});