javascript - 我应该总是在 JavaScript 的所有函数中返回 promise 吗?

标签 javascript promise

<分区>

我在想,在 JavaScript 的函数中总是返回 promises 是不是一种好方法?

让我们想象一下我们有一个验证用户名的函数的情况。主函数仅使用执行不同检查的其他 2 个函数。

请注意,所有函数名称只是示例。

// Returns a boolean
function validateUsername (username) {
  return validateUsernameFormat(username) &&
    isUsernameReserved(username);
}

// Returns a boolean
function validateUsernameFormat (username) {
  return typeOf(username) === 'string' &&
    username.match(/^\[a-z0-9]{8,20}$/);
}

// Returns a boolean
function isUsernameNotReserved (username) {
  return ['igor', 'kristina'].indexOf(username) === -1;
}

现在让我们想象一下,我们在未来通过调用 API 来检查给定的用户名是否已经存在于我们的数据库中来增强我们的验证。

// Now returns a promise
function isUsernameNotReserved (username) {
  return API.checkIfUserNameAlreadyExists(username);
}

这意味着我们现在还必须更改主 validateUsername 函数,因为它现在也需要返回 promise。这也可能意味着我们将不得不修改所有使用 validateUsername 函数的函数。

但是如果我们从头开始在 promises 中实现所有功能呢?

选项 A - 所有函数返回 promise

// Returns a promise
function validateUsername (username) {
  return validateUsernameFormat(username)
    .then(() => {
      return isUsernameReserved(username);
    });
}

// Returns a promise
function validateUsernameFormat (username) {
  return (
    typeOf(username) === 'string' && username.match(/^\[a-z0-9]{8,20}$/) ?
    Promise.resolve() : Promise.reject()
  );
}

// Returns a promise
function isUsernameNotReserved (username) {
  return (
    ['igor', 'kristina'].indexOf(username) === -1 ?
    Promise.resolve() : Promise.reject()
  );
}

现在,如果我们想通过异步 API 调用来扩充 isUsernameNotReserved,我们不需要更改任何其他内容。

选项 B - 只有调用另一个函数的函数返回 promise

此外,另一种选择是在调用另一个函数的 promise 中编写函数。在那种情况下,只有 validateUsername 应该从头开始写成一个 promise。

这是一个好方法吗?除了性能之外还有哪些缺点?


Upd:运行了一个简单的性能测试,虽然运行 consequent promises 速度较慢,但​​实际上应该没有什么区别,因为运行 100000 个后续函数需要大约 200 毫秒,而在 Chrome 中运行 1000 个需要大约 3 毫秒。在这里 fiddle https://jsfiddle.net/igorpavlov/o7nb71np/2/

最佳答案

Should I always return promises in all functions in JavaScript?

没有。

如果您有一个执行异步操作或可能执行异步操作的函数,那么从该函数返回一个 promise 是合理且通常好的设计。

但是,如果您的函数是完全同步的,并且没有合理的预见性认为它很快就会包含异步操作,那么您不应该从中返回 promise 的原因有很多:

  1. 异步代码编写和测试比同步代码编写和测试更复杂。所以,你真的不想让代码比它需要的更难编写和测试。如果您的代码可以同步(并且只返回一个正常值),那么您应该这样做。

  2. 每个 .then() 处理程序都会在下一个 tick 时被调用(保证是异步的)所以如果你进行一系列的同步操作并强制每个函数等待直到下一个 tick事件循环,你正在减慢代码执行。此外,您正在为每个函数调用添加垃圾收集器的工作(因为现在每个函数调用都有一个关联的 promise 对象)。

  3. 失去从同步函数返回正常值的能力是语言工具的一大倒退,您可以方便地使用这些工具来编写正常代码。您真的不想放弃每一个功能。

Now if we want to augment isUsernameNotReserved with asynchronous API call, we don't need to change anything else.

好的 API 设计会预测异步 API 在不久的将来是否相关或可能有用,并且仅在这种情况下才使 API 异步。因为异步 API 需要更多的编写、使用和测试工作,所以您不想不必要地让所有东西都异步“以防万一”。但是,明智的做法是预测您将来是否可能希望在 API 中使用异步操作。你只是不想在这里过火。请记住,将来可以添加或扩展 API 以支持出现的新异步事物,这样您就不必为了预先预测 future 可能发生的一切而将事物过度复杂化。您想取得平衡。

“平衡” 在这里似乎是正确的词。您希望在您的 API future 可能的需求与开发人员的简单性之间取得平衡。让一切都返回 promise 并没有取得适当的平衡,因为它选择了 future 无限的灵 active ,同时使不需要那么复杂的事情变得复杂。


在查看您的几个特定示例时:

validateUsernameFormat() 似乎永远不需要异步,所以我认为它没有理由返回 promise 。

isUsernameNotReserved() 看起来它在某些时候可能需要异步,因为您可能需要在数据库中查找一些数据以确定用户名是否可用。

不过,我可能会指出,在尝试创建用户名之前检查用户名是否可用可能会造成竞争条件。这种类型的检查可能仍然用作 UI 反馈,但在实际创建名称时,您通常需要以原子方式创建它,以便两个寻找相同名称的请求不会发生冲突。通常这是通过在数据库中创建名称来完成的,如果该名称不存在则创建成功,如果存在则失败。然后,负担由数据库(它所属的位置)来处理两个尝试创建相同用户名的请求的并发问题。

关于javascript - 我应该总是在 JavaScript 的所有函数中返回 promise 吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47699235/

相关文章:

javascript - OpenId Connect、查询 Microsoft Graph

javascript - 如何在 javascript 中使用 PluginResult 对象

javascript push() 失败

javascript - Angular promise .finally() 被乱序调用

javascript - 处理 promise 中的错误的正确方法是什么?

javascript - moment-timezone 将 utc 更改为默认时区

javascript - 查询结果未定义

javascript - 从另一个函数解析函数内的 promise

javascript - 期待一个 Jest 测试来解决,但不关心值(value)

javascript - 如何修复我的 promise return here for this function?