为什么我的异步函数返回 Promise 时会出现 ESLint 错误?

huangapple go评论112阅读模式
英文:

Why am I getting an ESLint error for async function with Promise return?

问题

Here's the translated code part without the code itself:

I have a Next.js application where I am defining an async function with Promise return and using it as an event handler for an HTML anchor element. However, when I try to run my code, ESLint is throwing the following error:

"Promise-returning function provided to variable where a void return was expected.(@typescript-eslint/no-misused-promises)"

Here's my code:

const { data: users, mutate: mutateUsers } = useSWR(
{ url: endpoints.users(), args: { id: userId || "" } },
({ args }) => getUsers(args.userId || ""));

const { data: myData, mutate: mutateData }   = useSWR(
    { url: endpoints.data(), args: { id: id || "" } },
    ({ args }) => getData(args.id || ""));

The ESLint error is being triggered by the use of the async keyword in the updateData function that is defined as a MouseEventHandler.

const updateData: MouseEventHandler<HTMLAnchorElement> = 
      async (e) => {
          e.preventDefault();
          await mutateUsers();        
          await mutateData();    
       };

And here's the HTML code that calls the updateData function:

<a className="alert-link" href="#" onClick={ updateData }>         
Update 
<myButton name=""/> 
</a>

I am not sure why ESLint is throwing this error and how to fix it. Can anyone suggest a solution to this problem? Thank you in advance for any help.

I have tried this solution:

const updateData: MouseEventHandler<HTMLAnchorElement> = (e) => {
   (async => { e.preventDefault();
   await mutateUsers();
   await mutateData();
 });

<a className="alert-link" href="#" onClick={ updateData }>        Update        <myButton name=""/>    </a>

but It doesn't work
英文:

I have a Next.js application where I am defining an async function with Promise return and using it as an event handler for a HTML anchor element. However, when I try to run my code, ESLint is throwing the following error:

> "Promise-returning function provided to variable where a void return was expected.(@typescript-eslint/no-misused-promises)"

Here's my code:

const { data: users, mutate: mutateUsers } = useSWR(
{ url: endpoints.users(), args: { id: userId || &quot;&quot; } },
({ args }) =&gt; getUsers(args.userId || &quot;&quot;));

const { data: myData, mutate: mutateData }   = useSWR(
    { url: endpoints.data(), args: { id: id || &quot;&quot; } },
    ({ args }) =&gt; getData(args.id || &quot;&quot;));`

The ESLint error is being triggered by the use of the async keyword in the updateData function that is defined as a MouseEventHandler.

 const updateData: MouseEventHandler&lt;HTMLAnchorElement&gt; = 
      async (e) =&gt; {
          e.preventDefault();
          await mutateUsers();        
          await mutateData();    
       };

And here's the HTML code that calls the updateData function:

&lt;a className=&quot;alert-link&quot; href=&quot;#&quot; onClick={ updateData }&gt;
Update
<myButton name=""/>
</a>`

I am not sure why ESLint is throwing this error and how to fix it. Can anyone suggest a solution to this problem? Thank you in advance for any help.

I have tried this solution:

const updateData: MouseEventHandler&lt;HTMLAnchorElement&gt; = (e) =&gt; {
   (async =&gt; { e.preventDefault();
   await mutateUsers();
   await mutateData();
 })
};

&lt;a className=&quot;alert-link&quot; href=&quot;#&quot; onClick={ updateData }&gt; Update &lt;myButton name=&quot;&quot;/&gt; &lt;/a&gt;

but It doesn't work

答案1

得分: 1

将一个async函数(或任何返回Promise的函数)作为回调传递给一个忽略回调的返回值的函数(即期望回调返回void的函数)是一个问题,因为这意味着该函数将调用回调而不处理Promise。特别是,它将忽略返回的Promise是否被拒绝,这将导致全局未处理的Promise拒绝错误,可能导致Node.js应用程序崩溃。

诚然,这不仅仅是Promise返回函数的问题,当你传递一个引发同步异常的回调时也存在同样的问题。函数会知道如何处理错误吗?1?异常会传播到哪里?1?(如果回调立即被调用,其实它可以直接返回给调用者)。

然而,对于浏览器中的事件处理程序,通常不是一个问题。首先,它不会导致Web应用程序崩溃。但主要是因为事件处理程序预期不会处理它们的错误,无论是同步还是异步的2。如果程序员关心的话3,他们会在async函数实现中处理错误,并返回一个永远不会拒绝的Promise。

因此,在这里基本上可以安全地忽略linter的错误。只需抑制错误。作为替代方案,你可以使用void运算符来明确忽略Promise:

const updateData: MouseEventHandler<HTMLAnchorElement> = (e) => {
  void (async => {
    //^^^^
    e.preventDefault();
    await mutateUsers();
    await mutateData();
  })();
//  ^^ 不要忘记调用 IIAFE
};
<button onClick={updateData}>

或者

const updateData = async (e) => {
  e.preventDefault();
  await mutateUsers();
  await mutateData();
};
<button onClick={e => void updateData(e)}>

1: 我们并不知道。我们(以及linter)实际上不能知道,因为TypeScript类型系统无法模拟异常。与忽略Promise的主要区别在于我们确切知道,这就是为什么这个规则存在的原因。
2: 实际上,异步函数预计会更容易出错,因为它们通常执行可能失败的IO操作,而同步事件处理程序很少引发异常。
3: 而且他们应该。应该。

英文:

Passing an async function (or any Promise-returning) function as a callback to a function that ignores the return value of the callback (i.e. expecting a callback that returns void) is a problem, because that means the function will invoke the callback and not handle the promise. In particular, it will ignore if the returned promise rejects, which will lead to a global unhandled promise rejection error which could crash a nodejs app.

Admittedly, this is not a problem just for promise-returning functions, it's the same problem when you pass a callback that throws a synchronous exception. Would the function know to handle the error?<sup>1</sup> Where would the exception propagate to? (It's actually fine if the callback was called immediately, it could just bubble back up to the caller).

However, this is generally not a problem for event handlers in the browser. For one, it doesn't crash a web application. But mainly, it's because event handlers are expected not to have their errors handled, whether they be synchronous or asynchronous<sup>2</sup>. If the programmer cared<sup>3</sup>, they would have handled the error inside the async function implementation, and would return a promise that would never reject.
So you can basically safely ignore the linter here. Just suppress the error. As an alternative, you can use the void operator to explicitly ignore the promise:

const updateData: MouseEventHandler&lt;HTMLAnchorElement&gt; = (e) =&gt; {
  void (async =&gt; {
//^^^^
    e.preventDefault();
    await mutateUsers();
    await mutateData();
  })();
//  ^^ don&#39;t forget to call the IIAFE
};
&lt;button onClick={ updateData }&gt;

or

const updateData = async (e) =&gt; {
  e.preventDefault();
  await mutateUsers();
  await mutateData();
};
&lt;button onClick={ e =&gt; void updateData(e) }&gt;

<sub>1: And we don't know. We (and the linter) cannot really know, since the TypeScript type system fails to model exceptions. The main difference to when a promise is ignored that we do know, and that's why this rule can exist.
2: Actually, asynchronous functions are expected to be more error-prone since they usually do IO which may fail, while synchronous event handlers rarely throw.
3: And they should. You should.</sub>

huangapple
  • 本文由 发表于 2023年3月4日 07:27:49
  • 转载请务必保留本文链接:https://go.coder-hub.com/75632670.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定