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

igo*_*lov 3 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;
}
Run Code Online (Sandbox Code Playgroud)

现在让我们假设我们通过调用API检查我们的数据库中是否已存在给定的用户名来增强我们的验证.

// Now returns a promise
function isUsernameNotReserved (username) {
  return API.checkIfUserNameAlreadyExists(username);
}
Run Code Online (Sandbox Code Playgroud)

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

但是,如果我们从头开始承诺所有功能呢?

选项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()
  );
}
Run Code Online (Sandbox Code Playgroud)

现在,如果我们想要isUsernameNotReserved使用异步API调用进行扩充,我们不需要更改任何其他内容.

选项B - 只有调用其他函数的函数才会返回promise

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

这是一个好方法吗?除性能外还有什么缺点?


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

jfr*_*d00 8

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

没有.

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

但是,如果你的函数是完全同步的,并且没有合理的预测认为这将很快包含异步操作,那么有很多理由说明为什么你不应该从它返回一个promise:

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

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

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

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

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

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


看一下你的几个例子:

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

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

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