【问题标题】:Optional Promise API可选的承诺 API
【发布时间】:2020-05-04 16:35:50
【问题描述】:

我有一个纯同步的验证库,但通常用作异步函数链的一部分。但是,我必须维护现有的同步 API,并希望将 promise API 设为可选。

我能否以某种方式(在运行时)检测函数是否是 Promise 链的一部分?

这很容易使用回调,因为您可以检查是否传入了回调。我知道我可以传入一个可选的 Promise 布尔值,但这似乎不优雅。

我还考虑过做一个回调接口并使用一个库将回调接口动态转换为基于 Promise 的接口。但是,我在 Haxe 工作,我希望将转换/抽象保持在最低限度。

我也明白,您可以将常规函数夹在 promise 之间,但在某些情况下,两者的行为会有所不同。

最终编辑对于为什么我不能只返回相同的值有很多困惑,第一个示例(如下)似乎没有帮助。请记住,这仍然是简化的。

//mix of sync with promise 
new Promise(function(resolve, reject){
  var safeToAdd = thingTracker.preflight(newThing);
  if(safeToAdd){
    return client.request.addThing(newThing); //send request to server
  } else {
    reject(newThing.errorMessages); //requires explicit reject, cannot just pass results along
  }
}).then(function(newThing){ //client and server both cool with newThing?
  thingTracker.save(newThing);
}).catch(function(errorMessages){ //handles errorMessages from client and server
  ui.show(errorMessages);
});

//pure promise
thingTracker.preflight(newThing).then(function(){
  return client.request.addThing(newThing); //sends request to server
}).then(function(newThing){  //client and server both cool with newThing?
  thingTracker.save(newThing);
}).catch(function(errorMessages){ //handles errorMessages from client and server
  ui.show(errorMessages);
});

(旧)编辑澄清(但不是真的):

function preflight(thing){
  var validity = thing === 42;

  if(promise){
    if(validity){
      return Promise.resolve(validity);
    } else {
      return Promise.reject(validity);
    }
  } else {
    return validity;
  }
}

显然我可以在then 的匿名函数中进行相同的检查,但这并不比直接使用同步接口好多少。另请注意,这是一个非常简单的示例,在实际函数中,thing 会产生副作用并产生消息。

编辑为了更好地说明我的观点,这里是gist 的样子。

【问题讨论】:

  • 你是在问如何检测一个函数是否返回一个promise,或者如何决定你的函数是否应该返回一个promise?
  • 如果我的函数应该返回一个承诺。
  • "一个纯同步的验证库" - 然后让它保持同步。没有理由在你身边使用承诺(相反,有理由不使用承诺)。不管调用来自哪里,同步函数也很容易集成到异步链中。

标签: javascript promise


【解决方案1】:

同步函数可以在承诺链中使用。如果您有一个像这样调用的同步 API 调用:

var info = myApi("foo");

这可以在承诺链中很好地使用:

someAsyncCall().then(myApi).then(someOtherFunction)

您无需使 myApi 异步或返回以这种方式使用的承诺。在具有同步函数的 Promise 链中唯一不能做的事情是它不能是链中的第一项,但它不需要是。由于它是同步的,因此如果有人希望它首先执行,则可以在启动链之前调用它。或者,在最坏的情况下,您可以这样做:

Promise.resolve().then(myAPI).then(someOtherFunction);

我能否以某种方式(在运行时)检测一个函数是否是 承诺链?

不,你不能(除非你明确地传递一些信息告诉它)并且你不需要为了在承诺链中使用它。您无需返回承诺即可成为.then() 处理程序。您可以只返回一个值,该值将成为链中该点的 Promise 链的值。

我也知道您可以将常规函数夹在中间 承诺,但在某些情况下行为会有所不同 两者之间......例如返回false与抛出消息。

我不清楚为什么行为会有所不同。如果返回 false 是您的正常同步行为,您可以在承诺链中做到这一点 - 链中的下一步只需要处理传递给它的 false 值,就像下一行同步代码会做。如果抛出异常是您的正常行为,您也可以在承诺链中做到这一点(承诺链会将异常转变为拒绝),以下代码可以决定它想要如何处理。一个很好的论点是,如果你的同步函数根据它是否是承诺链的一部分而表现不同,那会更糟。如果您看到两种不同行为的充分理由,那么您可能应该有两个不同的函数(可以以不同的方式记录)或传入一个决定行为的选项。

根据您在编辑中添加的代码发表评论

您似乎认为,当通过承诺链调用时,您需要返回已解决或被拒绝的承诺链。你不需要这样做。您可以只返回一个正常值,promise 链将继承您返回的值。除非您希望它成为 Promise 链中的第一个函数,否则实际上没有理由返回已解决或被拒绝的 Promise,但在这种情况下,它仍然不在 Promise 链中,因此您永远无法检测到这一点。而且,同步操作没有理由成为承诺链中的第一个操作。只需先调用同步操作,然后使用异步操作启动您的承诺链。

这是一个在承诺链中使用的同步函数的演示:

function log(str) {
    var div = document.createElement("div");
    div.innerHTML = str;
    document.body.appendChild(div);
}

// test async call
function delay(t, val) {
    return new Promise(function(resolve) {
        setTimeout(function() {
            resolve(val);
        }, t);
    });
}

function square(x) {
  return x * x;
}

log("Calculating the square of 9");
delay(500, 9).then(square).then(function(result) {
    log("Result = " + result);
});

或者,您甚至可以这样做来启动一个承诺链,并将您的同步操作作为链中的第一个操作:

function log(str) {
    var div = document.createElement("div");
    div.innerHTML = str;
    document.body.appendChild(div);
}

function square(x) {
  return x * x;
}

log("Calculating the square of 9");
Promise.resolve(9).then(square).then(function(result) {
    log("Result = " + result);
});

【讨论】:

  • @Indolering - 我根据您在编辑中添加的代码添加了一些 cmets。
  • 将两者混合的问题在于它只会节省一行(参见要点链接)。我想我能做的最好的事情就是创建一个新函数或传入一个 promise 参数。谢谢!
  • @Indolering,正如 jfriend00 解释的那样,你想象的可选 API 是多余的,除非在承诺链的最开始。
  • @jib 看到这个gist :)
  • 在链的开头,只需这样做:Promise.resolve(localTracker.preflight(newThing)).then(function(valid) { ... }
【解决方案2】:

我相信 promise 只有一个参数,所以不可能通过链传递 isPromise 布尔值,即使可以,你也必须有意识地添加参数,所以为什么不直接调用 async 函数...

function preflight(thing){
  return thing === 42;
}

function preflightAsync(thing){
  return new Promise(resolver);
  function resolver(resolve,reject){
    setTimeout(function foo(){
      preflight(thing) ? resolve(true,"this won't make it") : reject();
    },1000);
  }
}

// Promised
preflightAsync(42).then(doSomethingWithIt).catch(doSomethingWithIt);
preflightAsync(89).then(doSomethingWithIt).catch(doSomethingWithIt);

// Call Directly
doSomethingWithIt(preflight(42));
doSomethingWithIt(preflight(89));

function doSomethingWithIt(result,nada){
  result = result ? "BAM!!!" : "WAM!!!";
  console.log("doSomethingWithIt",result,nada);//doSomethingWithIt BAM!!! undefined
  return result;
}

这些示例使用新的原生 Promise 功能,浏览器支持有限。

【讨论】:

  • “我相信 Promise 只有一个参数,所以不可能通过链传递 isPromise 布尔值......” - 这是我在其他任何地方都没有提到过的东西!
  • 是的,我不知道为什么会这样。我更新了代码以显示第二个参数没有成功。这很奇怪,因为 $.ajax 用三个参数解析,为什么不是其余的呢?正确的?无论如何,这可能是特定 Promise 库中的一个缺点,也可能是它的一个特性,因为它使 Promise 更加兼容。
【解决方案3】:

正如我们从之前的答案中看到的,您不能对 Promises 使用完全相同的函数。在此处添加我的解决方法,不幸的是扩展了function 原型...

问题是,如果你想在 Promise 中同步使用相同的函数,arguments 的使用会有所不同。

举个例子——截断字符串的函数:

同步:

truncate('A very long string coming from a sync. operation', 10);

异步:

Promise.resolve('A very long string coming from a network request')
  .then( truncate(10) )

异步版本会使截断函数认为10 是字符串,您需要在每个函数中检查typeof 或进行ducktyping。 什么会起作用:

Promise.resolve('A very long string coming from a network request')
  .then( truncate.it(10) )

如果你之前扩展了函数原型:

Function.prototype.it = (function() {
  return function() {
    var args = arguments;
    return function(){
        Array.prototype.push.apply(arguments, args);
        return this.apply(null, arguments);
    }.bind(this);
  };
}());

function foo(a, b, c) {
  console.log('a is ' + a, ' | b is ' + b, ' | c is ' + c);
};

// LOGIC :    
foo(1, 2, 3);
// SAME AS
foo.it(2, 3)(1);
// OR
foo.it(3)(1, 2);
// OR pretty useful for .then()
Promise.resolve(1).then(foo.it(2, 3));

fiddle

【讨论】:

    猜你喜欢
    • 2015-12-10
    • 2015-11-22
    • 1970-01-01
    • 2018-07-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-06
    相关资源
    最近更新 更多