Javascript 使用Promise.all时光标可能出现的争用条件
在我正在工作的项目中,使用nodejs和mongo构建,有一个函数接收查询并根据提供给它的限制和偏移量返回一组数据。与此数据一起,函数返回一个总计数,说明数据库中存在的所有匹配对象。功能如下:Javascript 使用Promise.all时光标可能出现的争用条件,javascript,node.js,mongodb,Javascript,Node.js,Mongodb,在我正在工作的项目中,使用nodejs和mongo构建,有一个函数接收查询并根据提供给它的限制和偏移量返回一组数据。与此数据一起,函数返回一个总计数,说明数据库中存在的所有匹配对象。功能如下: // options carry the limit & offset values // mongoQuery carries a mongo matching query function findMany(query, options, collectionId) { const c
// options carry the limit & offset values
// mongoQuery carries a mongo matching query
function findMany(query, options, collectionId) {
const cursor = getCursorForCollection(collectionId).find(query, options);
return Promise.all([findManyQuery(cursor), countMany(cursor)]);
}
现在的问题是,当我给出一个大的极限尺寸时,我得到一个错误,说:
Uncaught exception: TypeError: Cannot read property '_killCursor' of undefined
起初,我认为我可能必须增加池的大小来解决这个问题,但在深入研究了一点之后,我发现上面的代码导致了竞争条件。当我将代码更改为:
function findMany(query, options, collectionId) {
const cursor = getCursorForCollection(collectionId).find(query, options);
return findManyQuery(cursor).then((dataSet) => {
return countMany(cursor).then((count)=> {
return Promise.resolve([dataSet, count]);
});
);
}
一切开始都很顺利。现在,从我对承诺的理解来看,所有的承诺都需要一系列的承诺,并且一个接一个地解决它们。如果承诺一个接一个地执行,那么Promise.all代码怎么会导致竞争条件,而承诺的链接不会导致竞争条件
我不能把我的头绕在它周围。为什么会发生这种情况?有一些方法可以用“纯”的方式编写此函数,以实现可伸缩性和测试 这是你关心的问题: 在我正在工作的项目中,使用nodejs和mongo构建,有一个函数接收查询并根据提供给它的限制和偏移量返回一组数据。与此数据一起,函数返回一个总计数,说明数据库中存在的所有匹配对象 注意:您需要处理边缘案例
const Model = require('path/to/model');
function findManyUsingPromise(model, query = {}, offset = 0, limit = 10) {
return new Promise((resolve, reject) => {
model.find(query, (error, data) => {
if(error) {
reject(error);
}
resolve({
data,
total: data.length || 0
});
}).skip(offset).limit(limit);
});
}
// Call function
findManyUsingPromise(Model, {}, 0, 40).then((result) => {
// Do something with result {data: [object array], total: value }
}).catch((err) => {
// Do something with the error
});
由于我没有多少信息可供使用,所以我对您想要实现的目标进行了假设,并使用Promise.all()提出了以下内容,以演示您应该如何使用Promise.all(这将解析不按特定顺序传递给它的承诺数组。因此,任何承诺中都不得依赖于承诺的执行顺序。)
“它接受一系列承诺,然后一个接一个地解决它们。”它对承诺没有任何作用。它创建了一个新的承诺,当所有承诺都得到解决时,该承诺就会得到解决(如果有拒绝,则拒绝).Promise Resolution取决于Promise实例。@YuryTarabanko我明白了。但承诺是一个接一个地解决的,对吗?还是它试图一次解决所有承诺?编辑:我认为我的引用是错误的,我想说的是,它逐个通过提供的数组,还是尝试一次执行所有承诺?
[findManyQuery(cursor),countMany(cursor)]
这两个函数都是快速连续执行的,也就是说,第二个函数没有等待第一个函数被解析。事实上,在这一行代码(—由两个函数返回两个值的数组)中,甚至没有任何东西知道函数是否返回承诺。“但是承诺是一个接一个地解决的,对吗?”不,为什么会是这样?承诺是随时解决的。承诺。所有承诺都不会影响个人承诺。”它试图一次执行所有承诺“你对承诺的思维模式是错误的。你认为它是一种可执行的东西。但它只是一个值(一个黑匣子,它会在一段时间后获得a值,或者无法获得a值).外部代码可能对黑盒获取值的内部机制一无所知。@YuryTarabanko Hm.好的。这意味着在promises.all()中同时调用两个函数/承诺.我想问的是,是否有任何承诺的执行流程。所有承诺都会遵循?我们为其提供了一系列承诺。这些承诺何时调用?这些承诺是否会一个接一个地快速调用?承诺。所有([1,2,3]).Call 1->然后Call 2->然后Call 3,还是像一次调用1,2,3一样?你不能从callback中返回
,但你的回答没有提到承诺.all()不起作用的原因。你刚刚发布了一个函数,它基本上按照我的解决方案执行我正在执行的操作。此外,数据将是一个有限的数组(接受限制)与查询匹配的对象,但我得到的totalCount是数据库中与查询匹配的对象总数,没有限制。hi@abhinav我发布了另一个回复,说明Promise.all()的使用。了解其他函数中的情况将有助于解决您的问题。
// A simple function to sumulate findManyQuery for demo purposes
function findManyQuery(cursors) {
return new Promise((resolve, reject) => {
// Do your checks and run your code (for example)
if (cursors) {
resolve({ dataset: cursors });
} else {
reject({ error: 'No cursor in findManyQuery function' });
}
});
}
// A simple function to sumulate countMany for demo purposes
function countMany(cursors) {
return new Promise((resolve, reject) => {
// Do your checks and run your code (for example)
if (cursors) {
resolve({ count: cursors.length });
} else {
reject({ error: 'No cursor in countMany' });
}
});
}
// A simple function to sumulate getCursorForCollection for demo purposes
function getCursorForCollection(collectionId) {
/*
Simulating the returned cursor using an array of objects
and the Array filter function
*/
return [{
id: 1,
language: 'Javascript',
collectionId: 99
}, {
id: 2,
language: 'Dart',
collectionId: 100
},
{
id: 3,
language: 'Go',
collectionId: 100
}, {
id: 4,
language: 'Swift',
collectionId: 99
}, {
id: 5,
language: 'Kotlin',
collectionId: 101
},
{
id: 6,
language: 'Python',
collectionId: 100
}].filter((row) => row.collectionId === collectionId)
}
function findMany(query = { id: 1 }, options = [], collectionId = 0) {
/*
First I create a function to simulate the assumed use of
query and options parameters just for demo purposes
*/
const filterFunction = function (collectionDocument) {
return collectionDocument.collectionId === query.id && options.indexOf(collectionDocument.language) !== -1;
};
/*
Since I am working with arrays, I replaced find function
with filter function just for demo purposes
*/
const cursors = getCursorForCollection(collectionId).filter(filterFunction);
/*
Using Promise.all([]). NOTE: You should pass the result of the
findManyQuery() to countMany() if you want to get the total
count of the resulting dataset
*/
return Promise.all([findManyQuery(cursors), countMany(cursors)]);
}
// Consuming the findMany function with test parameters
const query = { id: 100 };
const collectionId = 100;
const options = ['Javascript', 'Python', 'Go'];
findMany(query, options, collectionId).then(result => {
console.log(result); // Result would be [ { dataset: [ [Object], [Object] ] }, { count: 2 } ]
}).catch((error) => {
console.log(error);
});