简体   繁体   English

将一个监听器添加到Promise之后,我应该使用原始的Promise还是新的Promise?

[英]after adding a listener to a Promise, should I use the original promise or the new one?

I have some javasript code that takes an existing promise (say, the promise returned by fetch()) and adds value (say, then/catch listeners for debugging, or maybe more): 我有一些javasript代码,它们采用现有的promise(例如,fetch()返回的promise)并增加了价值(例如,然后/捕获侦听器以进行调试,或者可能更多):

let myFetch = function(url) {
  return fetch(url).then(function(value) {
    console.log("fetch succeeded: value=",value);
    return value;
  }.catch(function(reason) {
    console.log("fetch failed: reason=",reason);
    throw reason;
  });
};

I found myself modifying the above code so that the listeners are added only if some condition is true: 我发现自己修改了上面的代码,以便仅在满足某些条件时才添加侦听器:

let myFetch = function(url) {
  let promise = fetch(url);
  if (some condition) {
    promise = promise.then(function(value) {
      console.log("fetch succeeded: value=",value);
      return value;
    }.catch(function(reason) {
      console.log("fetch failed: reason=",reason);
      throw reason;
    });
  }
  return promise;
};

Now I'm wondering, does it really make sense for myFetch to return the new promise returned by "then" (actually catch which is shorthand for another "then") as above, or would it make more sense for it to return the original promise (with the added listeners)? 现在我想知道,myFetch返回上面的“ then”(实际上是catch,是另一个“ then”的简写)返回的新承诺真的有意义吗,还是让它返回原始内容更有意义承诺(与添加的侦听器)? In other words, I'm thinking of leaving out the second "promise =", so that the code will look like this instead: 换句话说,我正在考虑省略第二个“ promise =“,以便使代码看起来像这样:

let myFetch = function(url) {
  let promise = fetch(url);
  if (some condition) {
    promise.then(function(value) {
      console.log("fetch succeeded: value=",value);
      return value;
    }.catch(function(reason) {
      console.log("fetch failed: reason=",reason);
      throw reason;
    });
  }
  return promise;
};

Is that effectively different from the previous version? 这实际上与先前的版本不同吗? Is either version preferable, and if so, why? 哪个版本更可取,如果可以,为什么?

Well, if your success handler return s the value and your rejection handler throw s the error - then it is basically the identity transformation for the promise. 好吧,如果您的成功处理程序return s的值,而您的拒绝处理程序throw s的错误-那么这基本上是对promise的身份转换。

Not only do you not need to do that promise = promise.then you don't even need to return the values: 您不仅不需要执行promise = promise.then ,而且甚至不需要返回值:

let myFetch = function(url) {
  let promise = fetch(url);
  if (some condition) {
    promise.then(function(value) {
      console.log("fetch succeeded: value=",value);
    }.catch(function(reason) {
      console.log("fetch failed: reason=",reason);
    });
  }
  return promise;
};

That said, if you're using ES6 and let, you can use arrow functions which makes this a little nicer anyway: 就是说,如果您使用的是ES6和let,则可以使用箭头功能,无论如何,这会使它变得更好一点:

let myFetch = function(url) {
  let promise = fetch(url);
  if (some condition) {
    promise.then(value => console.log("fetch succeeded: value=",value))
          .catch(reason => console.log("fetch failed: reason=",reason));
  }
  return promise;
};

Some promise libraries like bluebird provide a tap utility for this. 一些承诺库(例如bluebird)为此提供了tap工具。 The only problem is that if ever fetch adds support for promise cancellation, you are breaking the chain with the if (some condition) handler if you're not chaining it. 唯一的问题是,如果fetch添加了对诺言取消的支持,那么如果不进行链接,则将使用if (some condition)处理程序来中断该链。

If your only use case is logging something in then / catch – it shouldn't matter as long as everything goes well. 如果您唯一的用例是在then / catch记录某些内容–只要一切顺利,就没有关系。 Things get more messed if you get an exception. 如果遇到异常,事情会变得更加混乱。 Consider these two examples: 考虑以下两个示例:

Return original promise 返还原始承诺

function myFetch() {
    let promise = new Promise(function (resolve, reject) {
        resolve(100);
    });
    promise.then(function () { throw new Error('x'); });
    return promise;
}

myFetch().then(function () {
    console.log('success!');
}).catch(function (e) {
    console.error('error!', e)
});

The result is success and the error thrown in the inner then might get swallowed in some promise libraries (although the most popular ones such as Bluebird handle this and you get additional error Unhandled rejection Error: x ). 结果是success并且内部抛出的错误then可能会在某些promise库中被吞没(尽管诸如Bluebird之类的最流行的库会处理此错误,并且您还会收到其他错误Unhandled rejection Error: x )。 The error might also get swallowed when using native Promises in some environments . 在某些环境中使用本机Promises时,错误也可能被吞没。

Returning modified promise 返回修改后的承诺

function myFetch() {
    let promise = new Promise(function (resolve, reject) {
        resolve(100);
    });
    promise = promise.then(function () { throw new Error('x'); });
    return promise;
}

myFetch().then(function () {
    console.log('success!');
}).catch(function (e) {
    console.error('error!', e)
});

Now the result is error! Error: x 现在结果是error! Error: x error! Error: x . error! Error: x

You're promise branching . 保证会分支 In the second case, you're effectively branching the promise chain into two promise chains, because once a caller calls myFetch : 在第二种情况下,您将有效地将promise链分为两个promise链,因为一旦调用方调用myFetch

myFetch("localhost").then(request => { /* use request */ } );

then promise will have had .then called on it twice (once inside myFetch to do the console logging, and again here). 然后promise会也曾有过.then调用它两次(一次内部myFetch做控制台记录,并再次在这里)。

This is fine. 这可以。 You can call .then on the same promise as many times as you like, and the functions will execute together in the same order whenever promise resolves. 您可以根据需要多次在同一诺言上调用.then ,并且每当promise解决时,函数将以相同的顺序一起执行。

But, importantly, each function represents a branch off of the original promise, independent of every other branch. 但是,重要的是,每个功能代表原始承诺的一个分支,独立于其他每个分支。 This is why you don't need to return or rethrow anything after your console.log : Nobody's listening on that branch, specifically, the caller of myFetch is not affected. 这就是为什么在console.log之后不需要返回或扔掉任何东西的原因:没人在那个分支上监听,特别是myFetch的调用者不受影响。

This is a good fit for logging IMHO, but there are subtle timing and error handling differences to be aware of when doing anything more: 这非常适合记录IMHO,但是在执行其他操作时,需要注意一些细微的时序和错误处理差异:

 var log = msg => div.innerHTML += msg + "<br>"; var myFetch = url => { var p = Promise.resolve({}); p.then(() => log("a")).then(() => log("b")); return p; } myFetch().then(() => log("1")).then(() => log("2")).catch(log); // a,1,b,2 
 <div id="div"></div> 

This emits a,1,b,2 . 这发出a,1,b,2 As you can see, there are two chains going on here, advancing in parallel. 正如您所看到的,这里有两个链条并行进行。 It makes sense when you think about when promise is resolved, but it can be surprising. 当您考虑何时promise时,这很有道理,但这可能令人惊讶。

The other subtlety is that error handling is also per branch (one branch will never fail another). 另一个微妙之处在于,错误处理也是针对每个分支的(一个分支永远不会使另一个分支失败)。 In fact, the above code has a bug. 实际上,上面的代码有一个错误。 Did you spot it? 你发现了吗? There should be a catch after .then(() => log("b")) , or errors about anything you do in that branch end up unhandled or swallowed in some environments. .then(() => log("b"))之后应该有一个catch ,否则您在该分支中执行的任何操作的错误最终都会在某些环境中被处理或吞噬。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM