preface

ECMAScript6 has been around for almost five years now. During those five years, ES6 revolutionized the modern front end, and Promise was a major presence, influencing countless front end libraries and apis. Promise is, so to speak, the blood of the modern front.

Despite 5 years of day and night, despite writing countless promises. Do we really understand an API that sometimes makes us feel good, comfortable to use, and sometimes stumbles us?

Strange situation 1: How to combine with circulation

I’m sure many developers will be unfamiliar with Promise at first: not knowing how to use it with loops. Such as:

FetchSomeData ().then((res) => {res.data.foreach ((item) => {doSomethingFunction(item); })}). Then (res => {// do something else})Copy the code

What’s wrong with this example?

The question is: The first THEN callback returns undefined, so the second then function does not wait for a doSomethingFunction(item); Performed. In fact, it doesn’t need to wait for anything, and can be found within doSomethingFunction(item); Execute after executing a few.

This is a very insidious error, because if res.data is small enough or if doSomethingFunction() executes fast enough, nothing is likely to be found.

How to solve it? You need promise.all ().

Promise.all()

fetchSomeData().then(res) => {
    return Promise.all(res.data.map(item) => {
        return doSomethingFunction(item); })}). Then (res => {// do something else})Copy the code

Promise. All takes an array of Promise objects as arguments, and calls then only when the array’s Promise states are Resolved or Rejected.

Strange situation 2: No return

fetchSomeData().then((res) => {
    doSomethingFunction(res); }). Then (res => {// do something else})Copy the code

The problem with this example is that the second then function takes undefined. Side effect is used to change rather than return.

Every Promise has a THEN method, and we can do three things in the THEN method:

  • Return another Promise
  • Return a value
  • Throw an error

Return a Promise

fetchSomeData().then((res) => {
    returngetId(res); }). Then (res => {// I can get id})Copy the code

Use return to return the second Promise, and in the second then method you get the ID. If there is no return, then getId() is just a side effect, and the second then method only gets undefined.

Return a value

Let’s say you want to do a cache on the ID to reduce the runtime.

fetchSomeData().then((res) => {
    if (idCache[id]) {
        return idCache[id];
    }
    returngetId(res); }). Then (res => {// I can get id})Copy the code

The correct id is returned regardless of whether the ID is cached or obtained asynchronously.

throw error

Throw Error makes promises more rigorous. If you want to do error handling when the user logs out:

fetchSomeData().then((res) => {
    if (logout) {
        throw new Error('User logged out');
    }
    if (idCache[id]) {
        return idCache[id];
    }
    returngetId(res); }). Then (res = > {/ / can I get id}). The catch (err = > error handling} {/ / do)Copy the code

The catch method catches the error.

Promise. Resolve () and Promise. Reject ()

Write the following frequently:

new Promise((resolve, reject) => {
    resolve(doSomething())
}).then(...)
Copy the code

In fact, I am not familiar with Promise, so I can use shorter sentences to express it

Promise.resolve

Promise.resolve(doSomething()).then(...)
Copy the code

Similarly promise.reject () can return a Promise that was immediately rejected

Promise.reject

Promise.reject(new Error('some error'))
Copy the code

Then ().catch() and then(resolveHandler, rejectHandler

Catch is then(null, function(err) {})

The following two pieces of code are equal

Promise (). The catch (err = > {/ / processing error}) promise (). Then (null, err = > {/ / processing error})Copy the code

But that doesn’t mean the following two pieces of code are equal

promise().then((res) => {
    returnotherPromise(res); }). Cathc (err = > {/ / can capture error}) promise (). Then (res = > {returnotherPromise(res); }, err => {// failed to catch error})Copy the code

Therefore, when using THEN (resolveHandler, rejectHandler), rejectHandler will not catch errors if they occur.

For this reason, catch methods are used whenever possible to catch errors.

Strange situation 5: How do I implement a series of promises

If you want to execute a series of promises, this is similar to the promise.all () method, but not in parallel. You might write the following code

function execute(promises) {
    var result = Promise.resolve();
    promise.forEach(promise => {
        result = result.then(promise);
    });
    return result;
}
Copy the code

Unfortunately, this does not perform as expected and is still executed in parallel.

This happens because the expectation is that you don’t want to operate on a series of promises. But according to the Promise specification, once a promise is created, it begins to execute.

Hence the promise factory function

function execute(promiseFactories) {
    var result = Promise.reslove();
    promiseFactories.forEach(promiseFactory => {
        result = result.then(promiseFactory);
    });
    return result;
}
Copy the code

The Promise factory function is very simple, just a function that returns a promise

function promiseFactory() {
    return promiseCreated();
}
Copy the code

This approach works because the Promise factory function does not create a promise until it is called. It works in the same way as the THEN function

Use of the then method

What do you think the output of the following code is?

Promise.resolve('foo').then(Promise.resolve('bar')).then((res) => {
    console.log(res);
})
Copy the code

If you think output bar, you’re wrong. It actually outputs foo!

Because when passed to the THEN () method is not a function, it actually executes then(null), so that the previous promise result cannot be passed to the second THEN method.

Promise.resolve('foo').then(null).then(res => {
    console.log(res) // foo
})
Copy the code

In short, you can pass a promise directly to the THEN method, but it doesn’t do what you expect. So here’s what you do

Promise.resolve('foo').then(() => {
    return Promise.resolve('bar')
}).then(res => {
    console.log(res); // bar
})
Copy the code

So, remind yourself: always pass the function to the THEN method

conclusion

Some people say: once born, twice cooked.

Through the above six times, I believe I am familiar with Promise as a relative.

We have a problem with promises

At the end

For more articles, please go to Github, if you like, please click star, which is also a kind of encouragement to the author.