Today I want to introduce you to a magic artifact, Promise. What are the problems that promise solves?

  1. Resolve callback hell (multiple Ajax nesting);
  2. Solve concurrent asynchrony (obtaining concurrent results at the same time);
  3. Chain call.

1. What is promise

Promise is divided into three states, which are:

  • Pendding: Initial state, which is neither successful nor failed.
  • This is a pity: which means that the operation will be completed successfully.
  • Rejected: Indicates that the operation fails.

2. The use of promise

Promise is a class, a built-in class that uses new Promise(), which has a single parameter, executor. Executor has two parameters: resolve, Rejected. Promise must have a method then, OnFulfilled, onRejected then has two parameters.

2.1 parameter

letThis will be a big pity. P = new Promise((resolve,reject)=>{// There is a parameter called executor, which is fulfilled successfully, and rejected fails}); P.tonight (onFulfilled, onRejected){// There is a big pity, onRejected}Copy the code

2.2 Basic use methods of Promise

2.2.1 Case 1:

let p = new Promise((resolve, reject)=> {
    resolve('Jj Lin'); }); p.then(data=>{ console.log(data); //'Jj Lin'
},err=>{
    console.log(err);
})
Copy the code
  • The above example takes the content of success, and the output data is the resolve argument

2.2.2 Case 2:

let p = new Promise((resolve, reject)=> {
    reject('Jay Chou');
    resolve('Jj Lin'); }); p.then(data=>{ console.log(data); },err=>{ console.log(err); //'Jay Chou'
})
Copy the code
  • In the previous example, reject() is also output, but reject() exists in promise. resolve(); “, walk is put in front of a state, as long as you go failure will not go success (as long as you go success will not go failure)

2.2.3 scenario 3

let p = new Promise((resolve, reject)=> {
    throw new Error('Jj Lin');
});
p.then(data=>{
    console.log('d',data);
},err=>{
    console.log('e',err); // Error: Lin Junjie})Copy the code
  • In the above example, when throwing an error, you also go reject, so when throwing an error, you also go reject, and no error is reported… Try… Catch)

Next comes the simulated handwritten promise (according to the Promise A +)

Class MyPromise {constructor(executor) {constructor(executor) {// The default state is the wait state this.status ='pending'; this.value = undefined; // Success argument this.reason = undefined; // Failed argumentletResolve = (data) => {// Note this, it is better to use the ES6 arrow functionif (this.status === 'pending') {this.value = data; this.status ='fulfilled'; }}let reject = (reason) => {
      if (this.status === 'pending') {
        this.reason = reason;
        this.status = 'rejected'; }} try {// An exception may occur when executing eg:throw new Error(); executor(resolve, reject); } Catch (e) {reject(e);} Catch (e) {reject(e); // Promise failed}}then(onFulFilled, onRejected) {
    if (this.status === 'fulfilled') {
      onFulFilled(this.value);
    }
    if (this.status === 'rejected') {
      onRejected(this.reason);
    }
  }
}
module.exports = Promise;
Copy the code
  • This is the simplest implementation, so the question is, if, in my promise, I have an asynchronous one, like a timer
let p = new Promise((resolve, reject)=> {
    setTimeout(()=>{
        reject('Jj Lin');
      },1000)
});
p.then(data=>{
    console.log('d',data);
},err=>{
    console.log('e',err); //e: Lin Junjie})Copy the code
  • The built-in promise will wait until the timer is executed for 1s before executing then, while the MyPromise completed by ourselves will not output any output at this time. The reason is that then is executed before the asynchronous timer, and the state of then at this time is pending, so no state of THEN will be executed. Therefore, we need to store the successful and failed functions of THEN in arrays respectively. OnResolvedCallbacks =[],onRejectedCallbacks=[], placed on the instance, so stored when the state is pending
if(this.status === "pending") {/ / equivalent to publish subscribe this. OnResolvedCallbacks. Push (() = > {onFulFilled (enclosing value); }) this.onResolvedCallbacks.push(()=>{ onRejected(this.reason); })}Copy the code
  • Equivalent to publish and subscribe mode, only called when the following state changes
let resolve = (data)=>{
    if(this.status === 'pending') {
        this.value = data;
        this.status = 'fulfilled'; this.onResolvedCallbacks.forEach(fn=>fn()); }}let reject = (err)=>{
    if(this.status === 'pending') {
        this.reason = err;
        this.status = 'rejected'; this.onRejectedCallbacks.forEach(fn=>fn()); }}Copy the code
  • That’s the basic implementation, but the main one is the chain call of Promise.

2.3 Chained invocation method of promise

let p = new Promise((resolve, reject) => {
  resolve();
});
p.then((data) => {
  // return new Promise((resolve,reject)=>{
  //   resolve(new Promise((resolve,reject)=>{
  //     reject('1234565'); //})) // the return value is promise. The return value of promise will be used as the outer layerthenThe parameters of thereturn123; // Return value is normal value, directly as the value of the outer layer next timethenThe parameters of thereturnnew Error; // Return normal value}, err => {console.log('p1:err', err);
}).then((data)=> {
  console.log('d',data);
},err=>{
  console.log('e',err);
})
Copy the code
  • If the return value of then is PROMISE, the return result of PROMISE will be used as the parameter of the outer THEN. If the return value is ordinary, the value will be used as the parameter of the outer THEN
  • So let’s keep going and what we see is that
let p = new Promise((resolve, reject) => {
    resolve('Jj Lin');
});
p.then().then()
p.then();
Copy the code
  • The two “THEN” methods in the example above have different results, so the “THEN” method calls and returns a new PROMISE (or not promise). The chain-writing method returns a new promise (instead of this), which can lead to failure and success. So then we’re going to return the new promise2
  • In this case, it deals with the relationship between promise2(p2 for short) and its return value X, resolvePromise(promise2, X, resolve, reject).
2.3.1 P2 return value x is also P2, as follows:
let promise2 = p.then(()=>{
    returnpromise2; //Chaining cycle detectedforPromise, will report an error loop reference. })Copy the code
  • In the example above, P2 will wait for its own result, and will not call successfully or fail, and will report a circular reference error

2.3.2 P2’s return value x may be an ordinary value, as follows:

let promise2 = p.then((data)=>{
    return 'Jj Lin';
},(err)=>{
    return 'z'
})
promise2.then((data) => { 
    console.log('d', data); }, (err) => {console.log()'e', err) 
});
Copy the code
  • This is a big pity. If the return value of P2 is a normal value (as long as there is no throw new Error()), no matter the onFulfilled function or onRejected, then the success state will be fulfilled in the next THEN

2.3.3 P2’s return value x may be a function, so it is considered a promise as follows:

let p2 =p.then((data) => {
  return new Promise((resolve, reject)=>{
    resolve(new Promise((resolve, reject)=>{
      resolve(1111)
    }))
  })
 }, (err) => {}).then((data) => {
  console.log('d', data); //d 1111 }, err => { console.log('e', err);
})
Copy the code
  • In the example above, the return value of P2 is a nested promise layer, which requires a recursive call to the resolvePromise method.
  • Next write the method resolvePromise according to the promise A +
functionResolvePromise (promise2, x, resolve, reject) {//promise returns a new promise. X is the return value of the promise2, and the other two are parametersif(promise2 === x) {// An error is reported, see 2.3.1return reject(new TypeError('Chaining cycle detected for promise')); } // x is not null or an object or function.if(x ! == null && (typeof x ==='object' || typeof x === 'function')) {
    letcalled; // Try {// try {// try {// trythenWhen an exception such as Object.defineProperty occurs, it is modifiedthenmethodslet then= x.then; / / take xthenMethod {then: {}}if (typeof then= = ='function') {// IfthenCall (x, y => {// If y is a promise, then continue to parse the promise recursivelyif(called) return;
          called = true; resolvePromise(promise2,y,resolve,reject); }, r => {// as long as it fails, it failsif (called) return;
          called = true;
          reject(r);
        });
      }else{ // thenResolve (x); resolve(x); } } catch (e) {if (called) return;
      called = true; reject(e); }}else{ resolve(x); // x is a normal value, for example, see 2.3.2}}Copy the code

2.4 Penetration of promise values

let p = new Promise((resolve, reject) => {
    resolve('Jj Lin'); }); p.then().then().then((data)=>{ console.log(data); // Jj Lin})Copy the code
  • In the above example, neither of the preceding two THEN parameters is passed or the parameter is null, but the data is still transmitted to the onFulfilled success state or onRejected failure state of the last THEN. Therefore, in the THEN method, parameters should be judged first
// This is a big pity, onFulFilled == typeof onFulFilled ==='function' ? onFulFilled : y => y;
    onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err; };
Copy the code

The native Promise then method is asynchronous, executing synchronous code first and then, for example:

p.then(()=>{console.log(1});
console.log(2);
Copy the code
  • In the example above, we can see that 2 to 1 is output first, so then is asynchronous, so each execution is wrapped with a timer

2.5 Other methods of Promise

2.5.1 catch

p.then().then().catch((err)=>{
console.log(err)
}).then((data)=>{
    console.log(data)
})
Copy the code

In fact, “catch” is a short form of “then”. If no successful callback is passed, “catch” is used, but when the “catch” is finished, it will continue to go down “then”, so the return is still a promise.

catch(rejected){
    return this.then(null, rejected)
}
Copy the code

2.5.2 resolve + reject

Promise.resolve = function(val) {
    return new Promise((resolve, reject)=> resolve(val))
}
Promise.reject = function(val){
    return new Promise((resolve, reject)=> reject(val))
}
Copy the code

2.5.3 all

The all method, natively, takes two promises as an array

p.all([promise1,promise2]).then((arr)=>{
    consolr.log(arr)
})
Copy the code
  • Among them, ARR is the content of all in which the promise is executed, and the sequence is in turn. Only when all the promises are successful, they will be successful, and one failure will be failed
Promise.all = function(promises) {
    return new Promise((resolve,reject)=> {
    let arr = [];
    let i = 0;
    function processData(index, data) {
        arr[index] = data;
        i++;
        if(i === promises.length) { resolve(arr); }}for(let i =0;i<promises.length;i++){
            promises[i].then(data=>{
               processData(i,data); 
            },reject);
        }
    })
}
Copy the code

2.5.4 race

The RACE method selects which promise to execute first and returns the result. A success is a success

Promise.race(promises) {
    return new Promise((resolve,reject)=>{
        for(leti = 0; i<promises.length; i++){ promises[i].then(resolve,reject) } }) }Copy the code

2.6 bluebird

  • Bluebird is to let asynchronous method direct promise, use the way
npm install bluebird
Copy the code
let read = bluebird.promisify(fs.readFile);
read('./a.txt'.'utf8').then(data=>{
    console.log(data);
})
Copy the code
  • Make the asynchronous method fs.readFilePromise. It’s like a layer of promise
function promisify(fn) {
    return function(... args){returnnew Promise((resolve, reject)=>{ fn(... args,function (err,data) {
                if(err) reject(err); resolve(data); }}})})Copy the code
let r = blusebird.promisifyAll(fs);
Copy the code
  • This method adds an Async to all methods on FS and promises them.

2.7 Q

  • We can promise it as well
npm install q
Copy the code
let Q = require('q');
let defer = Q.defer()
Copy the code
  • Here you can generate the Promise syntax sugar defer.

  • Await async…