building pipelines and avoid suffering
Before we start making amazing stuff with pipelines, let's recap everything about promises - I mean everything!
Promise
is an utility to execute tasks concurrently with
the main execution.
1 - Creating promises
There are 2 ways to create a promise:
new Promise
The constructor, Promise(f)
receivers a function
that will be executed in a scheduled manner.
In other to combine more promises,
this function f
receives 2 callbacks: resolve
and reject
.
By calling one of thoses 2 callback,
it may call a continuation, if defined,
using Promise.then(f)
, in case of resolve
gets called
or Promise.catch(g)
for reject
.
Creating a promise that has no continuations
new Promise(() => patchUser({ id: 1, updateName: "new name" }))
Creating a promise with continuation
new Promise(
(resolve, reject) =>
(Boolean(Math.ceil(Math.random() - 0.5))
? resolve
: reject)()
).then(
() => console.log("resolved")
).catch(
() => console.log("rejected")
)
Lifting a value into promise
As the promise constructor argument function receives 2 callbacks, they are also available to initialize a promise.
Promise.resolve(
1
).then(
(value) => assert(value === 1)
)
Promise.reject(
{ error: "some error" }
).catch(
(err) => assert(error == "some error")
)
2 - Continuations
You may think you know which path the execution will take, but you need to pay attention to every continuation.
Promise.then(f)
Addind a .then(f)
continuation, if the promise
was fulfilled and the result is resolve
,
the continuation is triggered calling the function f
with the result passed to resolve. If the promise calls
reject
, than the continuation is not triggered.
Promise.resolve(
1
).then(
(value) => assert(value === 1)
)
Promise.reject(
{ error: "some error" }
).then(
() => null /* function will not be called */
)
Return from .then(f)
You can return 3 values from .then(f)
:
- Any object
If you return a normal object from it,
it is the same as calling resolve(object)
.
This will trigger the next "then" continuation,
if defined.
Promise.resolve(
1
).then(
(value) => value + 1
).then(
(value) => assert(value === 2)
)
Promise.resolve(object)
You can return a promise from a continuation, in the case of returning "resolved" promise from a "then" continuation, it has the same effect as return a normal object.
Promise.resolve(
1
).then(
(value) => Promise.resolve(value + 1)
).then(
(value) => assert(value === 2)
)
Promise.reject(object)
As you can return a promise, it can also be a "rejected" one.
This will trigger a "catch" continuation.
Promise.resolve(
1
).then(
(value) => Promise.reject({ error: "some error" })
).catch(
({ error }) => assert(error == "some error")
)
Promise.catch(g)
Adding a .catch(g)
continuation, whenever
a promise is fulfilled and "rejected",
the function g
will be called with the object
of reject(object)
.
Promise.reject(
{ error: "some error" }
).then(
() => null /* function will not be called */
).catch(
({ error }) => assert(error == "some error")
)
Return from .catch(g)
Return resolved promises or normal objects
Return from catch is a little different, but interesting in some cases, and, there are a lot of interesting stuff to do with it.
Promise.resolve(object)
By returning a "resolved" promise from a catch
continuation, the next continuation to be "resolved"
and the .then(f)
will be triggered.
Promise.reject(
{ error: "some error" }
).catch(
() => Promise.resolve("ok")
).catch(
() => null /* function will not be called */
).then(
(value) => assert(value == "ok")
)
- Any object
Returning a normal value from a "catch"
continuation has the same behavior
as returning a Promise.resolve(object)
.
Promise.reject(
{ error: "some error" }
).catch(
() => "ok"
).catch(
() => null /* function will not be called */
).then(
(value) => assert(value == "ok")
)
Use cases for return resolved promises or normal objects
In some pipelines, there are cases there it may be possible to recover and continue the pipeline.
Example (default values):
Promise.resolve(
/* user recovered from localStorage */
user
).then(
/* we are going to kick out this user,
so we are going to reject here */
(user) => validateUserSession(user)
).catch(
/* we can return a object that simulates
a user but with almost no privileges
(better then use null) */
() => GuestUser()
).then(
(user) => assert(user.isAuthenticated() === false)
)