异步API是否应该同步抛出?

Jon*_*n M 9 javascript asynchronous api-design promise

我正在编写一个JavaScript函数,它发出HTTP请求并返回结果的承诺(但这个问题同样适用于基于回调的实现).

如果我立即知道为函数提供的参数是无效的,那么函数应该是throw同步的,还是应该返回一个被拒绝的promise(或者,如果你愿意的话,调用一个Error实例的回调)?

异步函数应始终以异步方式运行,特别是对于错误条件,这有多重要?是否确定throw,如果你知道程序是不是一个合适的状态的异步操作继续进行?

例如:

function getUserById(userId, cb) {
  if (userId !== parseInt(userId)) {
    throw new Error('userId is not valid')
  }

  // make async call
}

// OR...

function getUserById(userId, cb) {
  if (userId !== parseInt(userId)) {
    return cb(new Error('userId is not valid'))
  }

  // make async call
}
Run Code Online (Sandbox Code Playgroud)

Tim*_*lds 7

最终同步投掷与否的决定取决于你,你可能会找到那些争论任何一方的人.重要的是记录行为并保持行为的一致性.

我对此事的看法是你的第二个选择 - 将错误传递给回调 - 似乎更优雅.否则,您最终得到的代码如下所示:

try {
    getUserById(7, function (response) {
       if (response.isSuccess) {
           //Success case
       } else {
           //Failure case
       }
    });
} catch (error) {
    //Other failure case
}
Run Code Online (Sandbox Code Playgroud)

这里的控制流程有点令人困惑.

似乎if / else if / else在回调中使用单个结构并放弃周围环境会更好try / catch.


T.J*_*der 7

这很大程度上是一个意见问题。无论你做什么,都要始终如一地做,并清楚地记录下来。

我可以给你的一条客观信息是,这是 JavaScriptasync函数设计中大量讨论的主题,你可能知道,这些函数隐式地返回了其工作的承诺。您可能还知道函数中第一个或async之前的部分是同步的;它仅在其发送或返回时变为异步。awaitreturnawait

TC39 最终决定,即使函数的同步部分抛出错误,也async应该拒绝其 Promise,而不是引发同步错误。例如:

async function someAsyncStuff() {
    return 21;
}

async function example() {
    console.log("synchronous part of function");
    throw new Error("failed");
    const x = await someAsyncStuff();
    return x * 2;
}
try {
    console.log("before call");
    example().catch(e => { console.log("asynchronous:", e.message); });
    console.log("after call");
} catch (e) {
    console.log("synchronous:", e.message);
}
Run Code Online (Sandbox Code Playgroud)

在那里您可以看到,即使throw new Error("failed")位于函数的同步部分,它也会拒绝承诺而不是引发同步错误。

即使对于函数体中第一个语句之前发生的事情也是如此,例如确定缺少的函数参数的默认值:

async function someAsyncStuff() {
    return 21;
}

async function example(p = blah()) {
    console.log("synchronous part of function");
    throw new Error("failed");
    const x = await Promise.resolve(42);
    return x;
}
try {
    console.log("before call");
    example().catch(e => { console.log("asynchronous:", e.message); });
    console.log("after call");
} catch (e) {
    console.log("synchronous:", e.message);
}
Run Code Online (Sandbox Code Playgroud)

失败是因为当它运行blah代码以获取p我在调用中未提供的参数的默认值时,它尝试调用不存在的 。正如您所看到的,即使这样也会拒绝承诺而不是抛出同步错误。

TC39 可能采取另一种方式,让同步部分引发同步错误,就像这个非async函数所做的那样:

async function someAsyncStuff() {
    return 21;
}

function example() {
    console.log("synchronous part of function");
    throw new Error("failed");
    return someAsyncStuff().then(x => x * 2);
}
try {
    console.log("before call");
    example().catch(e => { console.log("asynchronous:", e.message); });
    console.log("after call");
} catch (e) {
    console.log("synchronous:", e.message);
}
Run Code Online (Sandbox Code Playgroud)

但经过讨论,他们决定一致拒绝承诺。

因此,在您决定如何在自己async执行异步工作的非函数中处理此问题时,这是需要考虑的一条具体信息。