简体   繁体   English

如何在不添加到 Promise.prototype 的情况下为 promises 创建一个链接 function 的 custom.andThen()?

[英]How to create a custom .andThen() chaining function for promises without adding to Promise.prototype?

I have defined a custom Either type and have functions returning a Promise(Either(left,right)) .我已经定义了一个自定义的Either类型,并且有返回Promise(Either(left,right))的函数。 To allow for chaining such functions I modified Promise's prototype like so:为了允许链接这些函数,我修改了 Promise 的原型,如下所示:

Promise.prototype.andThenE = function(andThen) {
  return this.then(p => {
    return p.fold(
      e => Promise.reject(Either.left(e)),
      s => andThen(s),
    );
  }).catch(c => c);
};

This allowed me to write code like:这让我可以编写如下代码:

const response = await functionXReturningPromiseEither(input).andThenE(functionYReturningPromiseEither).andThenE(...);

I'm aware that extending native objects' prototypes is bad practice and I'm trying to yank this andThenE and put it directly on my Either and am unable to get it to work/chain correctly.我知道扩展本机对象的原型是一种不好的做法,我正试图将其andThenE抽出并将其直接放在我的Either上,但无法使其正常工作/链接。

Here's my simple implementation of Either :这是我对Either的简单实现:

const Either = {};

Either.fromPromise = promise => {
  return promise.then(Either.right, Either.left);
};

Either.left = Either.failure = x => ({
  map: f => Either.left(x), //no-op: no mapping if "left" value
  fold: (l, r) => l(x), //apply "left" function to extract error value
  andThen: f => Either.left(x), //or flatMap or chain or join: Left(Left(x)) -> Left(x)
  dump: () => x, //for debugging
});

Either.right = Either.success = x => ({
  map: f => Either.right(f(x)), //map only if "right"
  fold: (l, r) => r(x), //apply "right" function to extract success value
  andThen: f => f(x), //or flatMap or chain or join: Right(Right(x)) -> Right(x)
  dump: () => x, //for debugging
});

If I do this it works flawlessly:如果我这样做,它会完美地工作:

const f = async (arr) => Either.right(arr.map(x => x+1));

const res = await Either.fromPromise(Promise.resolve([1])).andThenE(f).andThenE(f);
console.log(res.dump());    //[3] - is of type Either.right([3])

However, I am unable to get andThenE to become a member of the Either in any meaningful way.但是,我无法让andThenE以任何有意义的方式成为Either的成员。

I tried doing this to no avail:我尝试这样做无济于事:

PromiseEither = promise => ({
  andThenE: f => {
    return PromiseEither(promise.then(p => {
      console.log("inside promise");
      return p.fold(
        e => Promise.reject(Either.left(e)),
        s => s.andThen(f),
      );
    }).catch(c => c));
  },
});

Either.fromPromise = promise => {
 PromiseEither(promise.then(Either.right, Either.left))
}

This doesn't succeed:这不会成功:

const res = await Either.fromPromise(Promise.resolve([1])).andThenE(f).andThenE(f);
console.log(res); //prints PromiseEither's structure and await is meaningless

I've tried many other variations to know avail.我已经尝试了许多其他变体以了解其有效性。 I thought it was straightforward but after hours of banging my head, I haven't made any progress.我认为这很简单,但经过几个小时的敲打,我没有取得任何进展。 What may I be missing?我可能缺少什么? Is this even possible?这可能吗?

Link to fiddle链接到小提琴

First, I want to emphasise I would not do this.首先,我想强调我不会这样做。 I don't think this is a great idea since promises already have an error handling model which is what Either is typically used for when I've used it.我认为这不是一个好主意,因为 promises 已经有一个错误处理 model ,这是我使用它时通常使用的 Either 。

I would instead start from scratch if you want - and use .then as an interop point between your type (a continuation with different error handling) and promises.如果你愿意,我会从头开始 - 并使用.then作为你的类型(具有不同错误处理的延续)和承诺之间的互操作点。

That said the standard way to do what you want is with subclassing (yuck) or Symbol.species (even yuckier).那就是说做你想做的事情的标准方法是子类化(讨厌)或 Symbol.species(甚至更讨厌)。 I'll show the first option and leave the second option as an exercise to the avid reader:我将展示第一个选项,并将第二个选项作为练习留给狂热的读者:

class PromiseEither extends Promise {
  andThenE(f) {
    return PromiseEither(this.then(p => {
      console.log("inside promise");
      return p.fold(
        e => Promise.reject(Either.left(e)),
        s => s.andThen(f),
      );
    }).catch(c => c)); // I would not do this .catch if I were you btw
  }
});

}

I would caution against promise subclassing - it's very tricky.我会警告不要 promise 子类化——这非常棘手。 Even getting a copy of Promise from a new realm and mutating the prototype (like an iframe or vm in Node) is better IMO.即使从新的 realm 获取 Promise 的副本并改变原型(如 iframe 或 Node 中的 vm)也是更好的 IMO。

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

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