iTranslated by AI

The content below is an AI-generated translation. This is an experimental feature, and may contain errors. View original article
😎

Introduction to Pipeline Promises

に公開

Out of the blue, but have you ever written code like this?

const result= fun3(func2(await func1()))

With this syntax, it's hard to tell which function is taking which argument, and assigning variables for each function's result is quite a hassle, isn't it!

In such cases, you can use Promises to write it like this:

const result = await func1().then(func2).then(func3)

Doesn't this look much more readable? Yes, it does.
By using Promises as a pipeline in this way, you can improve code readability.

Additionally, using this method can make error handling easier.
For example, suppose you have code like the following.

try{
  const result= fun3(func2(await func1()))
}catch(e){
  console.error(e)
}

Rewriting it using the technique we just discussed looks like this:

const result = await func1().then(func2).then(func3).catch(console.error)

As you can see, it's much cleaner and easier to read.

Some of you might already be using the techniques mentioned so far.
Now, what about the following pattern?

const result = func3(func2(func1()))

That's right! Since the starting point func1 doesn't return a Promise, you can't start a pipeline as-is. If you're determined to use a pipeline-style syntax, you can write it like this:

const result = Promise.resolve(func1()).then(func2).then(func3)

This way, you can use the Promise pipeline and apply the error handling we discussed earlier.

Let's keep going. How can we use a pipeline effectively for the following pattern?

const result= fun4(func3(),func2(await func1()))

It's extremely hard to read, but you can see that func4 takes two arguments. Turning this into a pipeline looks like this:

const result = await func1()
  .then(func2)
  .then((value) => func4(func3(),value));

It's slightly improved, but still hard to read. So, let's create a function called curry.

const curry =
  <T, U, E>(func: (arg1: T, arg2: U) => E) =>
  (a: T) =>
  (b: U) =>
    func(a, b);

And by using this curry, you can write it like this:

const result = await func1().then(func2).then(curry(func4)(func3()));

Wonderful!!

And finally, let's turn the following case into a pipeline.

const result = fun4(await func3(), func2(await func1()));

The current curry function cannot properly handle the Promise returned by func3.
So, let's rewrite the curry function as follows:

const curry2 =
  <T, U, E>(func: (arg1: T, arg2: U) => E) =>
  (a: T | Promise<T>) =>
  (b: U | Promise<U>) =>
    Promise.all([a, b]).then(([a, b]) => func(a, b));

And by using this....

const result = await func1().then(func2).then(curry2(func4)(func3()));

😎😎 Perfect 😎😎

That's all for now. Have a great Promise life!!

Discussion