turnNine hints about Promises

As his colleagues had told him, Promise did an excellent job.

This article will give you some tips on how to improve your relationship with Promise.

1. You can return a Promise in. Then

Let me illustrate the most important point

Yes! You can return a Promise in. Then

Furthermore, the Promise of return will be resolved automatically in the next.then.

.then(r => { return serverStatusPromise(r); // Return {statusCode: 200} Promise}). Then (resp => {console.log(resp.statuscode); / / 200; Note the auto-resolved promise})Copy the code

2. A new Promise is automatically created each time.then is executed

If you’re familiar with javascript’s chained style, you should be familiar with it. But for a beginner, maybe not.

In a Promise, either you use.then or.catch to create a new Promise. This Promise is a combination of the Promise just called in the chain and the.then /.catch just added.

Let’s take a look at 🌰 :

var statusProm = fetchServerStatus();

var promA = statusProm.then(r => (r.statusCode === 200 ? "good" : "bad"));

var promB = promA.then(r => (r === "good" ? "ALL OK" : "NOTOK"));

var promC = statusProm.then(r => fetchThisAnotherThing());
Copy the code

The above Promise relationship can be clearly described in the flowchart:

promA
promB
promC

I like to think of.then as a large pipe where water stops flowing downstream when upstream nodes have problems. For example, if promB fails, the downstream node will not be affected, but if statusProm fails, all downstream nodes will be affected, i.e., rejected.

3. For callers,Promise ηš„ resolved/rejectedThe state is unique

I think this is one of the most important things to make Promise work. Simply put, if promises are shared across many different modules in your application, all the callers will be notified when promises return resolved/ Rejected state.

This also means that no one can change your Promise, so it’s safe to pass it on.

functionyourFunc() { const yourAwesomeProm = makeMeProm(); yourEvilUncle(yourAwesomeProm); Return yourAwesomeProm. Then (r => importantProcessing(r)); } functionyourEvilUncle(prom) { return prom.then(r =>Promise.reject("destroy!!" )); // Possible effects}Copy the code

As you can see from the example above, promises are designed to make themselves hard to change. As I said above, “Stay calm and keep the Promise”.

4. The Promise constructor is not a solution

I see a lot of developers who like to use the constructor style, and they think that’s the way Promise is. This is a lie, however, because the constructor API is similar to the previous callback API, and the habit is hard to break.

If you find yourself using it everywherePromise constructorThen you’re doing it wrong!

To really step forward and get rid of callbacks, you need to be careful and use the Promise constructor minimally.

Let’s look at the specifics of using the Promise constructor:

return new Promise((res, rej) => {
  fs.readFile("/etc/passwd", function(err, data) {
    if (err) return rej(err);
    return res(data);
  });
});
Copy the code

The Promise constructor should only be used if you want a callback to be a Promise. Once you’ve mastered this elegant way of making promises, they can become very attractive.

Let’s take a look at the redundant Promise constructor.

☠ ️ wrong

return new Promise((res, rej) => { var fetchPromise = fetchSomeData(.....) ; fetchPromise .then(data => { res(data); // Error!! }) .catch(err => rej(err)) })Copy the code

πŸ’– right

return fetchSomeData(...) ; // Correct!Copy the code

Wrapping promises in the Promise constructor is redundant and defeats the purpose of the Promise itself.

😎 Advanced Skills

If you are a NodeJS developer, I suggest you take a look at util.promisify. This method will help you convert Node style callbacks into promises.

const {promisify} = require('util');
const fs = require('fs');

const readFileAsync = promisify(fs.readFile);

readFileAsync('myfile.txt', 'utf-8')
  .then(r =>console.log(r))
  .catch(e =>console.error(e));
Copy the code

5. Use Promise. Resolve

Javascript provides the promise.resolve method, which is as succinct as the following example:

var similarProm = newPromise(res => res(5));
// ^^ η­‰δ»·δΊŽvar prom = Promise.resolve(5);
Copy the code

It can be used in a variety of ways, but one of my favorites is the ability to convert regular (asynchronous) JS objects into promises.

// Convert synchronous functions to asynchronous functionfoo() {returnpromise.resolve (5); }Copy the code

You can also make a secure wrapper when you’re not sure if it’s a Promise or a generic value.

functiongoodProm(maybePromise) { returnPromise.resolve(maybePromise); } goodProm(5).then(console.log); // 5var sixPromise = fetchMeNumber(6); goodProm(sixPromise).then(console.log); // 6 goodProm(Promise.resolve(Promise.resolve(5))).then(console.log); // 5, note that it will automatically resolve all promises!Copy the code

6. Use Promise. Reject

Javascript also provides the promise.reject method. An example like this is succinct:

var rejProm = newPromise((res, reject) => reject(5));

rejProm.catch(e =>console.log(e)) // 5
Copy the code

My favorite use is to use promise.reject in advance.

functionfoo(myVal) { if (! MVal) {returnPromise.reject(newError('myVal is required'))} returnnewPromise((res, rej) => {// Convert from your big callback to the Promise! })}Copy the code

Simply put, use promise.reject to reject any Promise you want.

In the following example, I use.then:

.then(val => { if (val ! = 5) { returnPromise.reject('Not Good'); }}).catch(e =>console.log(e)) // This is badCopy the code

Note: You can be likePromise.resolveAs in thePromise.rejectPass any value in. You often find this in failed promisesErrorBecause it is primarily used to throw an asynchronous error.

7. Use Promise. All

Javascript provides the promise.all method. Like… Such brevity, well, I can’t think of any examples 😁.

In the pseudo-algorithm, promise.all can be summarized as:

Take an array of promises and run them all together and then wait until they're all done and return a new array of promises where one of them fails or reject, can be captured.Copy the code

The following example shows all promises fulfilled:

var prom1 = Promise.resolve(5); var prom2 = fetchServerStatus(); / / return {statusCode: 200} Promise proimise.all ([prom1, prom2]).then([val1, val2] => {// Note that this is resolved to an array console.log(val1); // 5console.log(val2.statusCode); / / 200})Copy the code

The following example shows what happens when one of them fails:

var prom1 = Promise.reject(5); var prom2 = fetchServerStatus(); // Return the Promise proimise.all ([prom1, prom2]).then([val1, val2] => {console.log(val1); console.log(val2.statusCode); }).catch(e =>console.log(e)) // 5, go directly to.catchCopy the code

Note:Promise.allIs very clever! If one of the promises fails, it does not wait until all the promises are complete, but immediately suspends!

8. Don’t be afraid to reject, and don’t add redundancy after every. Then.catch

Do we often worry that mistakes will be swallowed up somewhere in between?

To overcome this fear, here’s a simple tip:

Let Reject handle the upstream function.

Ideally, the reject method should be the root of the application, with all the reject passed down.

Don’t be afraid to write like this

return fetchSomeData(...) ;Copy the code

Now if you want to deal with reject in a function, decide whether to resolve the problem or continue with reject.

πŸ’˜ solution to reject

Resolving reject is simple; whatever you return in. Catch is assumed to be resolved. However, if you return a Promise. Reject in a. Catch, the Promise will fail.

. Then (,) = > 5. Length) / / < -- complains here. Catch (e = > {return5; }). Then (r => {console.log(r); // 5 }) .catch(e => { console.error(e); // this method is never called :)})Copy the code

πŸ’” reject a reject

To reject a reject is simple. You don’t have to do anything. As I just said, let’s make it a problem for the other functions. In general, the parent function has a better way to handle reject than the current function.

The important thing to remember is that once you write the catch method, it means you are dealing with the error. This works in a similar way to synchronizing try/catch.

If you do want to intercept a reject :(I highly recommend not doing it!)

. Then (,) = > 5. Length) / / < -- complains here. Catch (e = > {errorLogger (e); // Do some error handling returnPromise.reject(e); // Reject it, yes, you can! }) .then(r => { console.log(r); // This.then (or any subsequent.then) will never be called because we use reject:}). Catch (e => {console.error(e); //<-- it becomes a catch method problem})Copy the code

.then(x,y) and then(x).catch(x)

The second callback function argument received by.then can also be used to handle errors. It looks a lot like then(x).catch(x), but the difference in how they handle errors is that they catch errors themselves.

I’ll illustrate this with the following example:

.then(function() { returnPromise.reject(newError('something wrong happened')); }).catch(function(e) { console.error(e); // something wrong happened }); .then(function() { returnPromise.reject(newError('something wrong happened')); }, function(e) {// This callback handles console.error(e) from before the current '. Then 'method; // No errors are printed});Copy the code

.then(x,y) comes in handy when you want to deal with errors from upstream promises rather than just adding them to.then.

Tip: It’s better to use the simple then(x).catch(x) 99.9% of the time.

9. Avoid. Then callback hell

This tip is relatively simple, try to avoid.then or.catch. Trust me, it’s easier to avoid than you think.

☠ ️ wrong

request(opts)
.catch(err => {
  if (err.statusCode === 400) {
    return request(opts)
           .then(r => r.text())
           .catch(err2 =>console.error(err2))
  }
})
Copy the code

πŸ’– right

request(opts)
.catch(err => {
  if (err.statusCode === 400) {
    return request(opts);
  }
})
.then(r => r.text())
.catch(err =>console.erro(err));
Copy the code

Sometimes we need so many variables in.then that we have no choice but to create a chain of.then methods.

.then(myVal => { const promA = foo(myVal); const promB = anotherPromMake(myVal); return promA .then(valA => { return promB.then(valB => hungryFunc(valA, valB)); // Very ugly! })})Copy the code

I recommend using the ES6 deconstruction method mixed with the Promise.all method to solve this problem.

.then(myVal => { const promA = foo(myVal); const promB = anotherPromMake(myVal); All ([PROM, anotherProm])}). Then (([valA, valB]) => {// Good use of ES6 to destruct console.log(valA, Return hungryFunc(valA, valB)})Copy the code

Note: You can also use async/await methods to solve this problem if your Node/browser/boss/consciousness allows it.

I really hope this article helped you understand Promise.