Javascript 使用Promise.all时光标可能出现的争用条件

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

在我正在工作的项目中,使用nodejs和mongo构建,有一个函数接收查询并根据提供给它的限制和偏移量返回一组数据。与此数据一起,函数返回一个总计数,说明数据库中存在的所有匹配对象。功能如下:

// 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);
});