正确使用 Promise.resolve

Correctly using Promise.resolve

我有一个返回承诺的 changePassword 函数:

function changePassword(userName, oldPassword, newPassword) {
  return new Promise(
    function(resolve, reject) {
      someAsyncFunction(userName, oldPassword, newPassword, function(ok) {
        if (ok) resolve(); else reject();
      }
    });
}

我需要修改这个函数,使 newPassword 可以是一个值或一个承诺:

// functionReturningNewPasswordValOrPromise is a function returning either a value
// or a promise that will resolve with a value 
function changePassword(userName, oldPassword, functionReturningNewPasswordValOrPromise) {
   return new Promise( ...

如果 functionReturningNewPasswordValOrPromise returns 一个值(新密码),一切如上进行。

但是,如果 functionReturningNewPasswordValOrPromise returns 是一个承诺,它将首先 运行(即提示用户)。如果已解决(即使用新密码字符串),changePassword 将使用该值继续解决。如果拒绝更改密码本身将拒绝。

我知道我需要以某种方式使用 Promise.resolve(thenable) 但不确定如何继续。

谢谢!

(我需要 ES6 中的答案,以便我可以在 IE 中使用 polyfill .. 没有 ES7)

我建议将 someAsyncFunction 包装到基于 promise 的函数中。 例如

function someAsyncFunctionP(userName, oldPassword, newPassword) {
  return new Promise(function(resolve, reject) {
    someAsyncFunction(userName, oldPassword, newPassword, function(err) {
      if(err) reject(err); else resolve();
    });
  });
}

然后让 newPassword 成为一个 Promise 你可以这样写。

function changePassword(userName, oldPassword, newPassword) {
  return Promise.resolve(newPassword).then(function(newPassword) {
    return someAsyncFunctionP(userName, oldPassword, newPassword);
  });
}

我想到的解决办法是:

function changePassword(userName, oldPassword, functionReturningNewPasswordValOrPromise) {
  var getNewPassPromise=functionReturningNewPasswordValOrPromise(email);
  return Promise.resolve(getNewPassPromise).then(function(newPassword) {
    return changePasswordWithVal(email, oldPassword, newPassword);
  });
}

// do the real work
function changePasswordWithVal(userName, oldPassword, newPassword) {
  return new Promise(
    function(resolve, reject) {
      someAsyncFunction(userName, oldPassword, newPassword, function(ok) {
        if (ok) resolve(); else reject();
      }
    });
}

拥有可能是或不是承诺的值通常是一个 anti-pattern,然后尝试检查它们是哪些,或者有特殊的逻辑来处理这个,或者将它们转换为承诺。这同样适用于可能 return 这样的 maybe-a-promise-maybe-not 值的函数。

所以我们需要回到调用函数的位置以及获取 newPassword 参数的位置。让我们假设它是这样的,使用 getNewPassword 当前设计为 return 值或承诺的函数:

changePassword(userName, oldPassword, getNewPassword())

第一个也是最好的方法是简单地更改 getNewPassword 的定义,因此它始终 return 是一个承诺——即使它已经解决了一些价值。那么你可以把上面写成

getNewPassword() . 
  then(newPassword => changePassword(userName, oldPassword, newPassword))

而且您根本不必更改 changePassword 的原始定义。

即使您不能更改 getNewPassword 的定义或签名,构建用于处理可能是也可能不是特定接口(例如 [=19=)的参数的逻辑似乎很笨拙].相反,我会在调用点通过

Promise.resolve(getNewPassword()) . 
  then(newPassword => changePassword(userName, oldPassword, newPassword))

换句话说,在调用的地方吸收getNewPassword的模糊设计的影响,而不是让下游的人担心它。

通用解决方案

如果你真的想处理接受参数的函数,这些参数可能是 promise 也可能不是 promise,你可以概括如下:

function promisify_arguments(fn) {
  return function(...args) {
    return Promise.all(args) . then(vals => fn(...vals));
  };
}

如果出于某种原因你有承诺但不支持 ES6,那么在 ES5 中:

function promisify_arguments(fn) {
  return function() {
    return Promise.all(arguments) . then(function(vals) {
      return fn.apply(0, vals));
    });
  };
}

这需要一个需要 non-promise 参数的函数,returns 是一个修改后的函数,可以用对某些或所有参数的承诺调用,并等待它的所有 promise-valued 参数解析,然后使用解析值作为参数调用原始函数,returning 一个其值为结果值的承诺。

您可以使用它来将您的 changePassword 函数转换为

var changePasswordWhereNewPasswordMayBePromise = promisify_arguments(changePassword);

然后使用 changePasswordWhereNewPasswordMaybePromise 而不是 changePassword。这具有潜在的优势,如果 userNameoldPassword 作为承诺提供,它会自动做正确的事情。

处理 newPassword 作为密码或承诺的简单答案是:

function changePassword(userName, oldPassword, newPassword) {
  return Promise.resolve(newPassword).then(password => new Promise((r, e) =>
      someAsyncFunction(userName, oldPassword, password, ok => ok ? r() : e())));
}

Promise.resolve(x) returns xx 是一个承诺 (*) 时,如果不是,则用值 x 解决承诺。

其他答案中有很好的建议,但我相信这就是所要求的。


*) 在大多数实现中,但不是全部(Safari 是一个例外),我观察到 Promise.resolve(p) === pp 是一个承诺,但这是一个细节。重要的一点是它在这种情况下就像最初的承诺一样。