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

时间:2017-12-07 16:12:13

标签: javascript promise

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

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

请注意,所有功能名称只是示例。

// 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函数的所有函数。

但是如果我们在承诺从头开始>

的所有功能呢?

选项A - 所有函数都返回承诺

// 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 - 只有调用其他函数的函数才会返回承诺

另外,另一种选择是在promises中编写函数来调用另一个函数。在这种情况下,只应从头开始将validateUsername写为承诺。

这是一个好方法吗?除了表现之外还有什么缺点?

更新:运行简单的性能测试,虽然运行后续承诺较慢,但实际上应该没有任何区别,因为运行100000个后续功能需要大约200毫秒,而运行1000需要大约3毫秒的Chrome。在这里摆弄https://jsfiddle.net/igorpavlov/o7nb71np/2/

1 个答案:

答案 0 :(得分:7)

  

我是否应该始终在JavaScript中的所有函数中返回promise?

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

但是,如果你的函数是完全同步的,并且没有合理的预期认为这很快就会包含异步操作,那么你就不应该从中返回一个承诺:

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

  2. 每个.then()处理程序在下一个tick上被调用(保证异步),所以如果你进行一系列的同步操作并强制每个函数等到事件循环的下一个滴答,你&# 39;重新放慢代码执行速度。此外,您还要为每个函数调用添加垃圾收集器的工作(因为现在它是与每个函数调用关联的promise对象)。

  3. 失去从同步函数返回正常值的能力是您可以方便地用来编写普通代码的语言工具的倒退。你真的不想在每一个功能上都放弃它。

  4.   

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

    良好的API设计可以预测异步API在不久的将来是否相关或可能有用,并且仅在这种情况下使API异步。因为异步API是更多的编写,使用和测试工作,所以你不希望不必要地将所有东西都做成异步"以防万一"。但是,预测您将来是否可能希望在API中使用异步操作是明智的。你不想在这里过火。请记住,API可以在未来添加或扩展,以支持出现的新异步事项,因此您不必过度复杂化,以便预先预测将来可能发生的所有事情。你想平衡。

    "余额" 在这里似乎是正确的词。您希望平衡API未来可能需求与开发人员的简单性。让一切都回归承诺并没有取得适当的平衡,因为它在未来选择了无限的灵活性,同时使复杂的事情变得复杂。

    查看几个特定的​​例子:

    validateUsernameFormat()似乎不太可能需要异步,所以我认为没有理由让它返回一个承诺。

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

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