ESLint 报告了我认为没问题的代码的承诺滥用

Posted

技术标签:

【中文标题】ESLint 报告了我认为没问题的代码的承诺滥用【英文标题】:ESLint reports a promise misuse for code I consider ok 【发布时间】:2021-11-04 04:05:49 【问题描述】:

在符号表实现中我有这个方法:

    public getAllSymbols(type?: typeof Symbol, localOnly = false): Promise<Set<Symbol>> 
        const promise = super.getAllSymbols(type ?? Symbol, localOnly);

        return new Promise(async (resolve, reject) => 
            try 
                let result = await promise;

                if (!localOnly) 
                    this.dependencies.forEach(async (dependency) => 
                        result = new Set([...result, ...await dependency.getAllSymbols(type, localOnly)]);
                    );
                

                resolve(result);
             catch (reason) 
                reject(reason);
            
        );
    

效果很好,但是 ESLint 报告了 2 个承诺滥用:

在函数参数中返回的 Promise 预期返回 void。 no-misused-promises

这段代码有什么问题,我必须如何编写它才能摆脱 linter 错误?

【问题讨论】:

可以想象问题在于执行器函数现在返回一个承诺(因为它被声明为异步),forEach 参数也是如此。这些函数都不应该返回任何东西。 我没有看到返回,除了函数返回的那个。看看eslint error description 就是我自己做的一个例子。 eslint错误描述告诉你,你的代码会报错——它是在错误代码的例子下。使函数异步自动将返回值包装在 Promise 中——即使你的函数中没有 return 语句——在这种情况下,它将返回 Promise......你总是可以等待异步函数。跨度> 【参考方案1】:

代码中的问题:

Executor 函数不应该是 async - 它是 anti-pattern

由于super.getAllSymbol(...) 已经返回了一个promise,所以不需要将它包装在promise 构造函数中——它是另一个anti-pattern。直接在super.getAllSymbol(...)返回的promise上调用then()方法

async-awaitforEach() 一起使用不会给您预期的结果,因为forEach() 的回调函数不会等待等待的promise 解决——它只会继续下一次迭代。

您可以使用Promise.all()map() 方法来获得预期的输出。

您也可以使用for-of 循环,但如果您不希望所有承诺都以顺序方式解决,则使用Promise.all() 会更好。

您的代码可以重写为(为简单起见删除类型):

public getAllSymbols(type, localOnly = false) 

     const promise = super.getAllSymbols(type ?? Symbol, localOnly);

     return promise
         .then(result => 
             if (!localOnly) 
                 return Promise.all(this.dependencies.map(dep => (
                    dep.getAllSymbols(type, localOnly))
                 )))
                 .then(resultArr => 
                     return new Set([...result, ...resultArr]);
                 );
             
             else 
                 return result;
                                    
         );

或者您可以使用async-await 语法:

public async getAllSymbols(type, localOnly = false) 

    const result = await super.getAllSymbols(type ?? Symbol, localOnly);

    if (!localOnly) 
        const resultArr = await Promise.all(this.dependencies.map(dep => ( 
            dep.getAllSymbols(type, localOnly)
        )));

        return new Set([...result, ...resultArr]);                         
    

    return result;                   

我删除了catch 块,因为在我看来,调用代码应该处理错误(如果有的话)。

上面的函数可以调用为:

getAllSymbols(...)
   .then(result =>  ... )
   .catch(error =>  ... );

或者你可以使用async-await语法:

try 
    const result = await getAllSymbols(...);
    ...
   
 catch (error) 
    // handle error

      

【讨论】:

注意:您可能需要修复集合合并的代码。 resultArr 不是符号数组,而是符号集数组。所以你需要在数组上循环来构造最终的集合。

以上是关于ESLint 报告了我认为没问题的代码的承诺滥用的主要内容,如果未能解决你的问题,请参考以下文章

ESLint 可以帮助你防止 Unhandled-Promise-Rejections 吗?

使用 ESLint `indent` 规则忽略模板文字中的缩进

vscode配置eslint自动保存功能不生效

如何让我的 Node.js MySQL 连接作为承诺工作?

带有 ESLint 的 TypeScript:解析错误:关键字“枚举”是保留的 eslint

ESlint:Promise和Async的配置