#javascript #ecmascript-6 #promise
#javascript #ecmascript-6 #обещание
Вопрос:
Я определил пользовательский Either
тип, и функции возвращают a Promise(Either(left,right))
. Чтобы разрешить объединение таких функций в цепочки, я изменил прототип 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);
};
Это позволило мне написать код, подобный:
const response = await functionXReturningPromiseEither(input).andThenE(functionYReturningPromiseEither).andThenE(...);
Я знаю, что расширение прототипов собственных объектов — плохая практика, и я пытаюсь выдернуть это andThenE
и поместить его прямо на себя 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
});
Если я сделаю это, это будет работать безупречно:
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])
Однако я не могу andThenE
стать участником Either
каким-либо значимым образом.
Я пытался сделать это безрезультатно:
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))
}
Это не удается:
const res = await Either.fromPromise(Promise.resolve([1])).andThenE(f).andThenE(f);
console.log(res); //prints PromiseEither's structure and await is meaningless
Я перепробовал много других вариантов, чтобы узнать, что это полезно. Я думал, что это просто, но после нескольких часов работы головой я не добился никакого прогресса. Чего мне может не хватать? Возможно ли это вообще?
Ответ №1:
Во-первых, я хочу подчеркнуть, что я бы этого не делал. Я не думаю, что это отличная идея, поскольку у promises уже есть модель обработки ошибок, для которой обычно используется Либо, когда я ее использовал.
Вместо этого я бы начал с нуля, если хотите, и использовал .then
в качестве точки взаимодействия между вашим типом (продолжение с другой обработкой ошибок) и promises .
Тем не менее, стандартный способ сделать то, что вы хотите, — это подкласс (гадость) или Symbol.species (еще более гадкий). Я покажу первый вариант и оставлю второй вариант в качестве упражнения для заядлого читателя:
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
}
});
}
Я бы предостерег от создания подклассов promise — это очень сложно. Даже получение копии Promise из новой области и изменение прототипа (например, iframe или виртуальной машины в Node) лучше IMO.
Комментарии:
1. Я надеялся, что это не связано с разделением Promise на подклассы, поскольку я думал , что это приведет к этому, но совсем не был уверен в этом TBH. Кроме того, не могли бы вы уточнить этот момент
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.
— я не уверен, что понимаю. Не могли бы вы привести пример?2.Объект с
then
(чтобы вы могли его ожидать), но который использует Либо в своей базовой структуреclass EitherPromiseLike extends Either
, либо реализует другую часть. Также забавно, что вы позвонилиflatMap
andThen
(поскольку я подозреваю, что «тогда» был вызван тогда по той же причине :])3. Также, если вы не знаете / не видели — в fantasy land есть множество таких игр
4. Я возился, чтобы помочь людям, не относящимся к FP, привыкнуть к этим идеям.
andThen
был более интуитивным в качестве первого шага. Вы имеете в виду, чтоEitherPromiseLike
будет иметь / переопределятьthen
метод?