首先,让我从我想要实现的目标开始。我想查询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()
我需要的记录?
答案 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
});
});