Javascript 在Node.js中实现锁定

Javascript 在Node.js中实现锁定,javascript,node.js,multithreading,synchronization,async-await,Javascript,Node.js,Multithreading,Synchronization,Async Await,前言: 为了解决我的问题,您必须具备以下方面的知识:线程安全、承诺、异步等待 对于不熟悉TypeScript的人来说,它只是带有类型注释的普通JavaScript(ES6) 我有一个名为excludeItems的函数,它接受一个项目列表(每个项目都是一个字符串),并为每个项目调用一个API(不包括该项目)。不要为同一项调用两次API,即使是在函数的不同执行中,这一点很重要,因此我将已排除的项保存在本地DB中 async function excludeItems(items: string[]

前言:

为了解决我的问题,您必须具备以下方面的知识:线程安全、承诺、异步等待

对于不熟悉TypeScript的人来说,它只是带有类型注释的普通JavaScript(ES6)


我有一个名为
excludeItems
的函数,它接受一个项目列表(每个项目都是一个字符串),并为每个项目调用一个API(不包括该项目)。不要为同一项调用两次API,即使是在函数的不同执行中,这一点很重要,因此我将已排除的项保存在本地DB中

async function excludeItems(items: string[]) {
            var excludedItems = await db.getExcludedItems();

            for (var i in items) {
                var item = items[i];
                var isAlreadyExcluded = excludedItems.find(excludedItem => excludedItem == item);

                if (isAlreadyExcluded) {
                    continue;
                }

                await someApi.excludeItem(item);
                await db.addExcludedItem(item);
            }
     }
该函数由其客户机异步调用多次,即在第一次执行完成之前,客户机调用该函数5次

具体情况如下:

excludeItems([]);
excludeItems(['A','B','C']);
excludeItems([]);
excludeItems(['A','C']);
excludeItems([]);
[{item: 'A'},
{item: 'B'},
{item: 'C'},
{item: 'A'},
{item: 'C'}]
在本例中,尽管Node.js是单线程的,但这里存在关键部分问题,我得到了错误的结果。这是执行该场景后本地数据库中的“excludedItems”集合:

excludeItems([]);
excludeItems(['A','B','C']);
excludeItems([]);
excludeItems(['A','C']);
excludeItems([]);
[{item: 'A'},
{item: 'B'},
{item: 'C'},
{item: 'A'},
{item: 'C'}]
如您所见,最后的“A”和“C”是冗余的(这意味着这些项也调用了两次API)

它是由于代码中的
wait
语句而发生的。每次到达wait语句时,都会在后台创建一个新的Promise,因此,尽管Node.js是单线程的,但等待执行的下一个异步函数正在执行,这样这个关键部分就会并行执行

为了解决这个问题,我实现了一种锁定机制:

var excludedItemsLocker = false;
async function safeExcludeItems(items: string[]) {
        while (excludedItemsLocker) {
            await sleep(100);
        }
    try {
        excludedItemsLocker = true;

        var excludedItems: string[] = await db.getExcludedItems();

        for (var i in items) {
            var item = items[i];
            var isAlreadyExcluded = excludedItems.find(excludedItem => excludedItem == item);

            if (isAlreadyExcluded) {
                continue;
            }

            await someApi.excludeItem(item);
            await db.addExcludedItem(item);
        }
    }
    finally {
        excludedItemsLocker = false;
    }
}

async function sleep(duration: number): Promise<Object> {
    return new Promise(function (resolve) {
        setTimeout(resolve, duration);
    });
}
var excludedItemLocker=false;
异步函数safeExcludeItems(项:字符串[]){
while(不包括dItemLocker){
等待睡眠(100);
}
试一试{
excludedItemsLocker=true;
var excludedItems:string[]=await db.getExcludedItems();
对于(项目中的var i){
var项目=项目[i];
var isAlreadyExcluded=excludedItems.find(excludedItem=>excludedItem==item);
如果(已排除在外){
继续;
}
等待某些API。排除项(项);
等待db.ADEXCLUDEDITEM(项目);
}
}
最后{
excludedItemsLocker=false;
}
}
异步函数睡眠(持续时间:number):承诺{
返回新承诺(函数(解析){
设置超时(解析、持续时间);
});
}
但是,由于某些原因,此实现不起作用。在关键部分我仍然得到了不止一个(所谓的)“线程”,这意味着它仍然被并行执行,我的本地数据库中充满了相同的错误结果。顺便说一句,
sleep
方法按预期工作,其目的只是为等待执行的下一个函数调用提供CPU时间

有人看到我的实现中出现了什么问题吗

顺便说一句,我知道我可以在不实现锁的情况下实现相同的目标,例如通过在循环中调用db.getExcludedItems,但我想知道我的锁实现被破坏的原因。

如果参数是:

['A','B','C']
和db.getExcludedItems()返回:

然后尝试在对象数组中查找字符串,该字符串将始终返回未定义的:

var isAlreadyExcluded = excludedItems.find(excludedItem => excludedItem == item);
只是想一想,因为我看不出锁定本身有任何问题,它应该按预期工作。

如果参数为:

['A','B','C']
和db.getExcludedItems()返回:

然后尝试在对象数组中查找字符串,该字符串将始终返回未定义的:

var isAlreadyExcluded = excludedItems.find(excludedItem => excludedItem == item);

只是想一想,因为我看不出锁定本身有任何问题,所以它应该能按预期工作。

我道歉。我忘了说db.getExcludedItems()返回一个字符串数组。不过,感谢您的帮助。无论如何,我在调试时看到两个(声称的)“线程”同时进入关键部分。我道歉。我忘了说db.getExcludedItems()返回一个字符串数组。不过,感谢您的帮助。无论如何,我在调试时看到两个(所谓的)“线程”同时进入关键部分。