Read Promise in One Article

Posted Jun 28, 20205 min read

Read Promises in One Article

What is Promise

$.ajax({
    success:(res) => {
        $.ajax({
            success:(res) => {
                $.ajax({
                    success:(res) => {
                        //...
                    }
                })
            }
        })
    }
})

This is a typical callback hell, not only is the code bloated and poorly readable, but it is also highly coupled and difficult to maintain. Code cannot be reused, and bugs are easy to hide.

Promise specification appeared to solve this problem.

Emphasize here, Promise is a solution, it is a specification.

ES6 natively provides Promise objects, which are often exposed to Promise-related operations in daily development. This article will introduce Promise usage methods and related principles, hoping to help.

Promises have three states:

  • Pending(waiting state)
  • Fulfilled(successful state)
  • Rejected(Failed state)

The characteristic of Promise is that the state can only be changed from Pending to Fulfilled or Rejected, and once changed, it will not be changed.

new Promise((resolve,reject) => {
    /*executor*/
})

Promise is a constructor(executor) with two parameters resolve and reject. This constructor is executed **immediately when the Promise is created. When the two functions resolve and reject are called, the Promise status is changed to fulfilled and rejected respectively, and the successful value is passed. Or the reason for failure. The executor usually performs some asynchronous operations internally. When the asynchronous operation is completed, you can call resolve or reject accordingly(perhaps success or failure) according to the execution result. If the executor throws an error, the Promise will also fail.

Promise prototype also has then and catch methods, and returns a Promise instance after the call, so it can be chained.

Each Promise instance comes with a then method, which has two parameters(onFulfilled, onRejected), which are callbacks when the Promise succeeds and fails.

let p = new Promise((resolve,reject) => {
    resolve('success');
})
p.then(value => {
    console.log(value); //success
}).then(value => {
    console.log(value); //When there is no return value, undefined
})

When a value or a Promise in a successful state is returned, the Promise returned by then is the successful state, and the successful state if there is no return value, and the parameter value of the callback function is undefind.

When an error is thrown or a failed Promise is returned, the Promises returned by then are all in a failed state.

let p = new Promise((resolve,reject) => {
    reject('fail');
})
p.then(() => {

},err => {
    console.log(err); //fail
});

The success/failure function passed by the then method, these two functions can return a promise; if the return is a promise, the promise state will be used as the result of the next then.

let p = new Promise((resolve,reject) => {
    resolve('success')
});
let p2 = new Promise((resolve,reject) => {
    resolve('success2')
})
p.then(value => {
    return p2; //The successful state of p2 will be used as the next then state
}).then(value => {
    console.log(value)
})

The return of the Promise.prototype.catch method is also a Promise, which is used in the same way as the then method and is used to handle exception catching situations. This method captures the nearest capture principle. The catch closest to the error will capture the error and process it, and will not be captured later.

If the failure status is triggered by the callback of the failure function in the then method, the catch method will not be captured.

let p1 = new Promise((resolve,reject) => {
    resolve('success');
});
p1.then(value => {
    throw'a new error';
}).catch(e => {
    console.log(e); //a new error
}).then(() => {
    console.log('after a catch the chain is restored');
})

The Promise.all method returns a new Promise object. The success state will be triggered when all the Promise objects in the parameter object are successful. Once any of the objects fails, the Promise object’s failure state will be triggered immediately. When the success state is triggered, an array containing all Promise return values ​​will be returned in order. If the failure state is violated, the error message of the first failed Promise object will be returned.

let p1 = new Promise((resolve,reject) => {
    setTimeout(() => {
        resolve('p1')
    },0)
});
let p2 = new Promise((resolve,reject) => {
    setTimeout(() => {
        resolve('p2')
    },0)
});
let promise = Promise.all([p1,p2]).then(value => {
    console.log(value); //['p1','p2']
})

The Promise.race method takes any first Promise that completes the state transition in the parameters as a success or failure status, and returns the Promise object.

let p1 = new Promise((resolve,reject) => {
    setTimeout(() => {
        resolve('p1')
    },1000)
});
let p2 = new Promise((resolve,reject) => {
    setTimeout(() => {
        reject('p2')
    },0)
});
Promise.race([p1,p2]).then((value) => {
    console.log(value)
},(err) => {
    console.log(err); //p2
})

Promise.resolve(value) returns a success status and passes the value to the corresponding then method;

Promise.reject(reason) returns a failure state, and passes reason to the corresponding then method.

Pros and cons

Advantages Disadvantages
Resolve callback Unable to monitor progress status
Chained call The new is executed immediately and cannot be canceled
Reduce nesting Internal errors cannot be thrown

Promise practice(Node environment)

1. Waiting state

const promise1 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('success')
    }, 1000)
})
const promise2 = promise1.then(() => {
    return'an error'
})

console.log('promise1', promise1)
console.log('promise2', promise2)

setTimeout(() => {
    console.log('promise1', promise1)
    console.log('promise2', promise2)
}, 2000)

2. Status change

const promise = new Promise((resolve, reject) => {
    resolve('success1')
    reject('error')
    resolve('success2')
})
promise.then((res) => {
    console.log('then:', res)
})
.catch((err) => {
    console.log('catch:', err)
})

3. Chain call

Promise.resolve(1)
    .then((res) => {
        console.log(res)
        return 2
    })
    .catch((err) => {
        return 3
    })
    .then((res) => {
        console.log(res)
    })

4. Chain call

const promise = new Promise((resolve, reject) => {
    setTimeout(() => {
        console.log('executor')
        resolve('success')
    }, 1000)
})

const start = Date.now()
promise.then((res) => {
    console.log(res, Date.now()-start)
})
promise.then((res) => {
    console.log(res, Date.now()-start)
})
//The constructor of Promise is executed only once, and immediately; the then method of promise can be called multiple times, and a new instance of promise is returned each time.

const promise = new Promise((resolve, reject) => {
    setTimeout(() => {
        console.log('executor')
        resolve('success')
    }, 1000)
})
const promise2 = promise.then(res => {
    console.log(res);
})
promise2.then(res => {
    console.log(res)
})

5. Error capture

Promise.resolve()
    .then(() => {
        return new Error('error!!!')
    })
    .then((res) => {
        console.log('then:', res)
    })
    .catch((err) => {
        console.log('catch:', err)
    })

Promise.resolve()
    .then(() => {
        throw new Error('error'); //return Promise.reject('error')
    })
    .then((res) => {
        console.log('then:', res)
    })
    .catch((err) => {
        console.log('catch:', err)
    })

The then or catch method returns an error object and will not be captured. Only when an error is thrown or a failure state is returned will the capture be caused.

6. Quote error

const promise = Promise.resolve()
  .then(() => {
    return promise
  })
promise.catch(e => {
    console.log(e)
})
//[TypeError:Chaining cycle detected for promise #<Promise>]

The return value cannot be the promise instance itself, because the instance is not built at this time, causing an endless loop, so a type error is thrown.

7. Event Ring

process.nextTick(() => {
    console.log('nextTick')
})
Promise.resolve()
    .then(() => {
        console.log('then')
    })
setImmediate(() => {
    console.log('setImmediate')
})
console.log('end')

8. Penetration

Promise.resolve(1)
    .then(2)
    .then(Promise.resolve(3))
    .then(console.log)

The parameter of then and catch method is a function, if it is not a function, it will penetrate

Promise A+ Specification

Specification: https://promisesaplus.com/

This website introduces the standard methods that need to be implemented if Promises are to be implemented. When multiple libraries implement their own Promise class, in order to achieve the corresponding effect, the specification provides an implementation standard, and provides the promises-aplus-tests test package test implementation capability.

A follow-up article will introduce how to implement your own Promise class, so stay tuned!
Front end preferred