正确使用Promise.resolve

时间:2016-01-08 22:09:29

标签: javascript promise es6-promise

我有一个changePassword函数返回一个promise:

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

我需要修改此函数,以便newPassword可以是值或promise:

// 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返回一个值(新密码),则一切都按上述步骤进行。

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

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

THX!

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

4 个答案:

答案 0 :(得分:2)

具有可能或不是承诺的值通常是反模式,然后尝试检查它们是什么,或者具有处理它的特殊逻辑,或者将它们转换为承诺。这同样适用于可能返回这样的可能 - 许诺 - 可能不是值的函数。

因此,我们需要返回调用函数的位置以及获取newPassword参数的位置。让我们假设它是这样的,使用getNewPassword函数,该函数目前被设计为返回值或承诺:

changePassword(userName, oldPassword, getNewPassword())

第一种也许是最好的方法是简单地改变getNewPassword的定义,以便它总是返回一个承诺 - 即使它已经用某种值解决了。然后你可以把上面的内容写成

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

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

即使您无法更改getNewPassword的定义或签名,构建处理参数的逻辑似乎也很笨拙,该参数可能是也可能不是对changePassword等特定接口的承诺。相反,我会在呼叫点通过

执行此操作
Promise.resolve(getNewPassword()) . 
  then(newPassword => changePassword(userName, oldPassword, newPassword))

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

一般解决方案

如果你真的想处理那些带有可能会或可能不会有承诺的参数的函数,你可以概括一下:

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));
    });
  };
}

这需要一个期望非承诺参数的函数,并返回一个修改过的函数,该函数可以通过对某些或所有参数的promise进行调用,并等待其所有的promise值参数解析,然后调用原始函数将值解析为参数,返回其值为结果值的promise。

您可以使用此功能将changePassword功能转换为

var changePasswordWhereNewPasswordMayBePromise = promisify_arguments(changePassword);

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

答案 1 :(得分:2)

处理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)是承诺(*)时,

x会返回x,而当承诺x时,承诺会以值Promise.resolve(p) === p解析。

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

*)在大多数实现中,但不是全部(Safari是一个例外),当p是一个承诺时,我发现test test test ,但这是一个细节。重要的是它在这种情况下就像最初的承诺一样。

答案 2 :(得分:1)

我建议将someAsyncFunction包装成基于promise的函数。 e.g。

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

然后,为了让newPassword成为一个承诺,你可以写下这个。

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

答案 3 :(得分:0)

我提出的解决方案是:

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();
      }
    });
}