使用Promise.all时光标可能出现的竞态条件

时间:2019-09-03 22:13:39

标签: javascript node.js mongodb

在我正在使用Node.js和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的了解,它需要一系列承诺并一个接一个地解决它们。如果Promise.all代码一个接一个地执行,那么Promise.all代码怎么会导致竞争状态,而Promise的链接不会导致竞争条件。

我无法把头缠住它。为什么会这样?

2 个答案:

答案 0 :(得分:1)

由于我需要处理的信息很少,因此我假设了您要实现的目标,并使用Promise.all()提出了以下内容,只是为了说明您应如何使用Promise.all(这将解决承诺的数组没有特定的顺序传递。因此,任何Promise中都不得依赖于Promises的执行顺序。Read more about it here)。

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

答案 1 :(得分:-1)

有一些方法可以以“纯”方式编写此功能以进行可伸缩性和测试。

所以这是您的关注点: 在我正在使用nodejs和mongo构建的项目中,有一个函数可以接受查询并根据提供给它的限制和偏移量返回数据集。函数会连同这些数据一起返回总数,说明数据库中存在的所有匹配对象。

注意:您需要注意边缘情况。

VSTest.Console.exe