Understanding Promise

Understanding Promise

What is Promise

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

This is a typical callback hell, not only the code is bloated, the readability is poor, but also the coupling is too high, and it is not easy to maintain. The code can't be reused, and it's easy to hide bug s.

Promise specification is to solve this problem.

To emphasize here, Promise is a solution, it's a specification.

ES6 natively provides Promise objects, which are often exposed to Promise related operations in daily development. This article will introduce the use methods and related principles of Promise, hoping to be helpful.

Promise has three states:

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

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

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

Promise is a constructor with two parameters of resolve and reject. This constructor is executed immediately when the project is created. When the resolve and reject functions are called, they change the state of the project to completed and rejected respectively, and pass the successful value or the reason of failure. Generally, some asynchronous operations are executed inside the executor. When the asynchronous operations are completed, resolve or reject can be called accordingly according to the execution results (may succeed or fail). If the executor throws an error, the promise will also turn to the failed state.

The Promise prototype also has the then and catch methods, which return a Promise instance after calling, so it can be called by chain.

Each project instance has a then method, which has two parameters (onFulfilled,onRejected), which are the callback when the project succeeds or fails.

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

When a value or a Promise in the success state is returned, the Promise returned by then is in the success state. If there is no return value, it is also in the success state. The parameter value of the callback function is undefind.

When an error is thrown or a failure state Promise is returned, then the Promise returned is the failure 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 a promise is returned, the promise status will be 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 success state of p2 will be the state of next then
}).then(value => {
    console.log(value)
})

Promise.prototype.catch Method return is also a promise, which is used in the same way as the then method to handle exception capture. This method capture belongs to the nearest catch principle. The catch closest to the error will capture and process the error, and will not be captured in the future.

If the failure state is triggered by the failure function callback in the then method, the catch method will not capture it.

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');
})

Promise.all Method returns a new promise object. The success state will be triggered only when all promise objects in the parameter object are successful. If any one of the promise objects fails, the failure state of the promise object will be triggered immediately. When the success state is triggered, an array containing all promise return values is returned in order. If the failure state is violated, the error information 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']
})

Promise.race The method takes any first promise in the parameter that completes the state transition as a success or failure state, 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 state and passes value to the corresponding then method;

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

Pros and cons of Promise

advantage shortcoming
Resolve callback Unable to monitor progress status
call chaining New immediate and cannot be cancelled
Reduce nesting Internal error cannot be thrown

Promise exercise (Node environment)

1. Waiting status

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. State 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, each time returning a new promise instance.
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 return of an error object by the then or catch method is not captured, but only when an error is thrown or a failure state is returned.

6. Quotation 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, resulting in a dead cycle, 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 parameters of then and catch methods are a function. If they are not functions, they will be penetrated

Promise A + specification

standard: https://promisesaplus.com/

This website introduces the standard methods needed to implement Promise. When multiple libraries implement their own Promise class, in order to achieve the corresponding effect, the specification provides the implementation standard, and provides the test implementation ability of promises aplus tests test package.

In the future, we will introduce how to implement our Promise class. Please look forward to it!

Tags: Javascript

Posted on Sat, 27 Jun 2020 20:18:12 -0400 by js_280