我在使用Promises和process.nextTick()回调的node.js应用程序中有一个有效的递归函数。我很好奇这如何/可以与异步等待一起工作。
我尝试了一些不同的操作,但是无论我做了什么,异步功能都会在所有nextTick回调完成之前返回到调用函数。
不起作用(从快速路由中删除删除缓存)
const deleteCache = async () => {
try {
const cacheRef = fsDb.collection('Cache');
return await deleteDocsBatch(cacheRef, 30);
} catch (e) {
console.error('error in deleteCache:' + e);
}
};
const deleteDocsBatch = async (cacheRef, batchSize) => {
try {
// get all the cached docs, limit to 30 to avoid potential memory issues
const snapShot = await cacheRef.limit(batchSize).get();
if (snapShot.size === 0) { return; }
const batch = fsDb.batch();
snapShot.docs.forEach((doc) => {
batch.delete(doc.ref);
});
await batch.commit();
process.nextTick(() => {
deleteDocsBatch(cacheRef, batchSize);
});
} catch (e) {
console.error('error in deleteDocsBatch:' + e);
}
};
工作:
function deleteCollection (batchSize) {
var collectionRef = fsDb.collection('Cache');
var query = collectionRef.orderBy('__name__').limit(batchSize);
return new Promise((resolve, reject) => {
deleteQueryBatch(fsDb, query, batchSize, resolve, reject);
});
}
function deleteQueryBatch (db, query, batchSize, resolve, reject) {
query.get()
.then((snapshot) => {
// When there are no documents left, we are done
if (snapshot.size === 0) {
return new Promise((resolve, reject) => { resolve(0); });
}
// Delete documents in a batch
var batch = db.batch();
snapshot.docs.forEach((doc) => {
batch.delete(doc.ref);
});
return new Promise((resolve, reject) => {
batch.commit().then(() => {
resolve(snapshot.size);
})
.catch(reject);
});
}).then((numDeleted) => {
if (numDeleted === 0) {
resolve();
return;
}
// Recurse on the next process tick, to avoid
// exploding the stack.
process.nextTick(() => {
deleteQueryBatch(db, query, batchSize, resolve, reject);
});
})
.catch(reject);
}
是否可以使用async await使用nexttick()编写此递归函数?
原始的Firestore代码示例:
https://firebase.google.com/docs/firestore/manage-data/delete-data
答案 0 :(得分:-1)
好吧,所以利用以下事实:当您通过.then()
将任何内容附加到Promise时,它将在下一个刻度中运行。换句话说,您的process.nextTick
甚至在原始代码中也没有必要。在最坏的情况下,您将进入递归调用,但立即退出。永远不要超过深度1。
await
是.then()
的语法糖。无论如何,代码都会转换为.then()
系列。所以这个
await deleteDocsBatch(cacheRef, batchSize);
代替
process.nextTick(() => {
deleteDocsBatch(cacheRef, batchSize);
});
应该足够。尽管如上所述,该函数的初始同步部分(即直到第一次等待)将递归运行。因此,如果您确实要确定,则可以通过
await Promise.resolve();
await deleteDocsBatch(cacheRef, batchSize);
问题在于您告诉解释器“嘿,这是一个异步点,请执行其他操作,像什么都不做,好吗?”。
还要注意,await new Promise(res => process.nextTick(res));
是替代方法。虽然太过分了。
一个例子:
async function p1() {
console.log('interrupt');
};
async function p2() {
console.log('1');
await Promise.resolve();
console.log('2');
};
p2();
p1();
因此您可以看到这两个功能实际上是同步的。 p2
并不是因为里面有await
。并且await
强制将下面的所有内容移至下一个刻度,从而允许p1
在两者之间运行。 p2
功能等效于
function p2() {
console.log('1');
return Promise.resolve().then(() => {
console.log('2');
});
};
,输出为:
1
interrupt
2
另一个例子。这非常快地超过了最大递归深度:
async function go(i)
{
console.log(i);
go(i+1);
}
go(0);
不是。曾经。
async function go(i)
{
console.log(i);
await Promise.resolve();
go(i+1);
}
go(0);
第二个代码实际上使用了恒定数量的内存。
结论:函数内的任何(可达)await
都将中断递归调用。代码如下所示:
const deleteDocsBatch = async (cacheRef, batchSize) => {
try {
// get all the cached docs, limit to 30 to avoid potential memory issues
const snapShot = await cacheRef.limit(batchSize).get();
if (snapShot.size === 0) { return; }
const batch = fsDb.batch();
snapShot.docs.forEach((doc) => {
batch.delete(doc.ref);
});
await batch.commit();
await deleteDocsBatch(cacheRef, batchSize);
} catch (e) {
console.error('error in deleteDocsBatch:' + e);
}
};
,您唯一需要担心的递归问题是停止条件(例如:是否存在该函数永不结束的情况?)。该代码不会占用您的内存。