Why did you use promise first? What problems did Promise solve? Usually our callbacks are solved by putting them into callback functions, which creates callback hell, so promise comes in and solves callback hell. Promise addresses not only the callback hell problem, but also uniform error handling

The first step is simple implementation

  • Implementation of multiple THEN and asynchronous execution, so easy.
Tip: Resolve or Reject simply changes the status and value of a promise.Copy the code
const PENDING = 'PENDING'
const RESOLVED = 'RESOLVED'
const REJECTED = 'REJECTED'
class Promise{
    constructor(executor){
        this.value = ' '
        this.status = PENDING
        this.onResolvedCallBacks = [] // If the initial value is array and the executor is asynchronous, then can be stored in multiple functions
        this.onRejectedCallBacks = []
        const resolve = (value) = > {
            if (this.status === PENDING) {
                this.value = value
                this.status = RESOLVED
                this.onResolvedCallBacks.forEach(fn= > fn()) / / release}}const reject = (reason) = > {
            if (this.status === PENDING) {
                this.value = reason
                this.status = REJECTED
                this.onRejectedCallBacks.forEach(fn= > fn()) / / release}}try {
            executor(resolve,reject)
        }catch(e){
            reject(e)
        }
    }
    
    then(onFulfilled, onRejected) {
        if (this.status === RESOLVED) {
            onFulfilled(this.value)
        }
        if (this.status === REJECTED) {
            onRejected(this.value)
        }
        if (this.status === PENDING) { / / subscribe
            this.onResolvedCallBacks.push(() = > { // Function for easy processing, decorative mode, slice programming
                onFullfilled(this.value)
            })
            this.onRejectedCallBacks.push(() = > {
                onRejected(this.value)
            })
        }
    }
}
module.exports = Promise
Copy the code
  • Test the
1) Test asynchronous execution of new Promise((resolve, reject) => {setTimeout(() => {resolve(1)}, 1000); }). Then (value => {console.log(value)}) // 1 2) reject) => { setTimeout(() => { resolve(1) }, 1000); }) promise.then(value => { console.log('value1: ', value) }) promise.then(value => { console.log('value2: ', value) }) // value1: 1 // value2: 1Copy the code

The chain-call implementation of the second step then

  • Chain calls to THEN (mainly by the return value of the then callback method)
const PENDING = 'PENDING'
const RESOLVED = 'RESOLVED'
const REJECTED = 'REJECTED'
// This method handles the promise2 state
function resolvePromise(x, promise2, resolve, reject) {
    if (x === promise2) {
        reject(new TypeError('Chaining cycle detected for promise #<Promise>'))}// Determine x to perform resolve or reject
    // Check if x is a promise: it is of type object, then method, and type function
    if (typeof x === 'object'&& x ! = =null || typeof x === 'function') {
        try {
            let then = x.then
            if (typeof then === 'function') {
            	/ / x for promise
                then.call(x, y= > {
                    resolvePromise(y, promise2, resolve, reject)
                }, r= > {
                    reject(r)
                })
            } else {
            	// {then: 1}
                resolve(x)
            }
        }catch(e) {
        	// {then: throw new Error('err')}
            reject(e)
        }
    } else {
    	// x is a normal value
    	resolve(x)
    }
}
class Promise{
    constructor(executor){
        this.value = ' '
        this.status = PENDING
        this.onResolvedCallBacks = [] // If the initial value is array and the executor is asynchronous, then can be stored in multiple functions
        this.onRejectedCallBacks = []
        const resolve = (value) = > {
            if (this.status === PENDING) {
                this.value = value
                this.status = RESOLVED
                this.onResolvedCallBacks.forEach(fn= > fn()) / / release}}const reject = (reason) = > {
            if (this.status === PENDING) {
                this.value = reason
                this.status = REJECTED
                this.onRejectedCallBacks.forEach(fn= > fn()) / / release}}try {
            executor(resolve,reject)
        }catch(e){
            reject(e)
        }
    }
    
    then(onFulfilled, onRejected) {
        let promise2 = new Promise((resolve, reject) = > {
            if (this.status === RESOLVED) {
                setTimeout(() = > {
                    try {
                    	/ / the key
                        let x = onFulfilled(this.value)
                        resolvePromise(x, promise2, resolve, reject)
                    }catch(e) {
                        reject(e)
                    }
                }, 0)}if (this.status === REJECTED) {
                setTimeout(() = > {
                    try {
                    	/ / the key
                        let x = onRejected(this.value)
                        resolvePromise(x, promise2, resolve, reject)
                    }catch(e) {
                        reject(e)
                    }
                }, 0)}if (this.status === PENDING) { / / subscribe
                this.onResolvedCallBacks.push(() = > { // easy to handle in the function
                    setTimeout(() = > {
                    	try {
                        	/ / the key
                            let x = onFulfilled(this.value)
                            resolvePromise(x, promise2, resolve, reject)
                        }catch(e) {
                            reject(e)
                        }
                    }, 0)})this.onRejectedCallBacks.push(() = > {
                	setTimeout(() = > {
                        try {
                        	/ / the key
                            let x = onRejected(this.value)
                            resolvePromise(x, promise2, resolve, reject)
                        }catch(e) {
                            reject(e)
                        }
                    }, 0)})}})return promise2
    }
}
module.exports = Promise
Copy the code
  • Test the
  new Promise((resolve, reject) => {
      resolve(1)
  }).then(value => {
      console.log('value1: ', value)
      return value
  }).then(value => {
      console.log('value2: ', value)
  }, reason => {
      console.log(reason)
  })
  // value1:  1
  // value2:  1
Copy the code

The third step is to solve a few problems

  • 1. The value of penetration
  • 2. Resolve the case where the value of resolve is Promise
  • 3. Solve the problem of introducing other non-standard promises, resulting in multiple calls
const PENDING = 'PENDING'
const RESOLVED = 'RESOLVED'
const REJECTED = 'REJECTED'

function resolvePromise(x, promise2, resolve, reject) {
    if (x === promise2) {
        reject(new TypeError('Chaining cycle detected for promise #<Promise>'))}// Determine x to perform resolve or reject
    // Check if x is a promise: it is of type object, then method, and type function
    if (typeof x === 'object'&& x ! = =null || typeof x === 'function') {
    	let called; // Solve problem 3
        try {
            let then = x.then
            if (typeof then === 'function') {
            	/ / x for promise
                then.call(x, y= > {
                	if(called)return
                    called = true
                    resolvePromise(y, promise2, resolve, reject)
                }, r= > {
                    if(called)return
                    called = true
                    reject(r)
                })
            } else {
            	// {then: 1}
                resolve(x)
            }
        } catch(e) {
        	// {then: throw new Error('err')}
            if(called)return
            called = true
            reject(e)
        }
    } else {
    	// x is a normal value
        resolve(x)
    }
}
class Promise{
    constructor(executor){
        this.value = ' '
        this.status = PENDING
        this.onResolvedCallBacks = [] // If the initial value is array and the executor is asynchronous, then can be stored in multiple functions
        this.onRejectedCallBacks = []
        const resolve = (value) = > {
            if (this.status === PENDING) {
            	// If value is an instance of Promise, solve problem 2 by recursively resolving this.value
                if (value instanceof Promise) {
                    return value.then(resolve, reject)
                }
                this.value = value
                this.status = RESOLVED
                this.onResolvedCallBacks.forEach(fn= > fn()) / / release}}const reject = (reason) = > {
            if (this.status === PENDING) {
                this.value = reason
                this.status = REJECTED
                this.onRejectedCallBacks.forEach(fn= > fn()) / / release}}try {
            executor(resolve,reject)
        }catch(e){
            reject(e)
        }
    }
    
    then(onFulfilled, onRejected) {
    	// If then has no parameters, the argument is assigned a default value function
        onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : v= > v
        onRejected = typeof onRejected === 'function' ? onRejected : e= > {throw e}
        let promise2 = new Promise((resolve, reject) = > {
            if (this.status === RESOLVED) {
                setTimeout(() = > {
                    try {
                    	/ / the key
                        let x = onFulfilled(this.value)
                        resolvePromise(x, promise2, resolve, reject)
                    }catch(e) {
                        reject(e)
                    }
                }, 0)}if (this.status === REJECTED) {
                setTimeout(() = > {
                    try {
                    	/ / the key
                        let x = onRejected(this.value)
                        resolvePromise(x, promise2, resolve, reject)
                    }catch(e) {
                        reject(e)
                    }
                }, 0)}if (this.status === PENDING) { / / subscribe
               this.onResolvedCallBacks.push(() = > { // easy to handle in the function
                    setTimeout(() = > {
                    	try {
                        	/ / the key
                            let x = onFulfilled(this.value)
                            resolvePromise(x, promise2, resolve, reject)
                        }catch(e) {
                            reject(e)
                        }
                    }, 0)})this.onRejectedCallBacks.push(() = > {
                	setTimeout(() = > {
                    	try {
                        	/ / the key
                            let x = onRejected(this.value)
                            resolvePromise(x, promise2, resolve, reject)
                        }catch(e) {
                            reject(e)
                        }
                    }, 0)})}})return promise2
    }
}
module.exports = Promise
Copy the code
  • Verify that our promises comply with the PromiseA+ specification
  1) First, add the following code to the promise implementation code:Promise.defer = Promise.deferred = function () {
        let dfd = {};
        dfd.promise = new Promise((resolve, reject) = > {
            dfd.resolve = resolve;
            dfd.reject = reject;
        });
        return dfd;
    }

  2NPM install -g promises-aplus-tests if the current promise source file is promise.js3Promises -aplus-tests promise.js: promises-aplus-tests promiseCopy the code
  • Implementation of Promises -aplus-tests promise.js results
. The value is' 1 'with' Number. Prototype 'modified to have a' then 'method ✓ already fulfilled - immediately fulfilled -fulfilled ✓ Eventually - pity ✓ already-rejected chrome immediately-rejected chrome eventually-rejected 872 passing (16s)Copy the code

At this point, our promise is fulfilled and in compliance with the PromiseA+ specification.

Do some exercises to consolidate

  console.log(1);
  async function async () {
      console.log(2);
      await console.log(3); // Promise.resolve(console.log(3)).then(()=>{4})
      console.log(4)
  }
  setTimeout(() => {
      console.log(5);
  }, 0);
  const promise = new Promise((resolve, reject) => {
      console.log(6);
      resolve(7)
  })
  promise.then(res => {
      console.log(res)
  })
  async();
  console.log(8);
Copy the code
  Promise.resolve().then(()=>{ // then1
      console.log(1);
      Promise.resolve().then(()=>{ // then11
          console.log(11);
      }).then(()=>{
          console.log(22); // then22
      }).then(()=>{
          console.log(33); // then33
      })
  }).then(()=>{ // then2
      console.log(2);
  }).then(()=>{ // then3
      console.log(3);
  })
Copy the code
Async function async1(){// node 11 async function async1(){console.log('async1 start'); // Other versions of Node may parse await out of two then to await async2(); // Promise.resolve(async2()).then(()=>{console.log('ok')}) } async function async2(){ console.log('async2'); } console.log('script start'); setTimeout(() => { console.log('setTimeout') }, 0); async1(); new Promise(function(resolve){ console.log('promise1'); resolve() }).then(function(){ console.log('script end') })Copy the code

Resolve, promise.reject, all, race, cancel promise, finally, catch, defer(defer object)

  • Promise. Resolve to achieve
  Promise.resolve = function(val) {
      return new Promise((resolve, reject) => {
          resolve(val)
      })
  }
Copy the code
  • Promise. Reject
  Promise.reject = function(val) {
      return new Promise((resolve, reject) => {
          reject(val)
      })
  }
Copy the code
  • Promise. All implementations
Promise.all = function(arr) { return new Promise((resolve, reject) => { let index = 0 let values = [] for (let i = 0; i<arr.length; i++) { Promise.resolve(arr[i]).then(r => { values[i] = r if(++index === arr.length){ resolve(values) } }, Y = > {reject (y)})}})} / / cases Promise. All ([1, 2, 3]). Then (v = > {the console. The log (v)})Copy the code
  • Promise. Race
Promise.race = function(arr) { return new Promise((resolve, reject) => { for(let i=0; i<arr.length; i++){ Promise.resolve(arr[i]).then(r=>{ resolve(r) }, Y = > {reject (y)})}})} / / cases Promise. Race ([1, 2, 3]). Then (r = > {the console. The log (r)})Copy the code
  • Cancel the promise
function wrap(promise) { let abort = null let abortPromise = new Promise((resolve, reject) => { abort = reject }) promise = Promise.race([promise, AbortPromise]) promise. Abort = abort return promise} reject) => { setTimeout(() => { resolve(1) }, 2000); }) let newPromise = wrap(promise) setTimeout(() => { newPromise.abort(12) }, 1000); newPromise.then(r => { console.log(r) }, y => { console.log(y) })Copy the code
  • The finally realize
If it's wrong at the end, take the wrong value at the end, if it's wrong at the beginning, take the wrong value at the beginning. If both are true, // resolve(x) && resolve(y) => then resolve(x) // resolve(x) && reject(y) => Then Reject (y) // reject(x) && resolve(y) => then reject(x) // reject(x) && reject(y) => then reject(y)Copy the code
Promise.prototype.finally = function(cb) { return this.then(value=> { return Promise.resolve(cb()).then(()=>value, Y =>y)}, reason=>{return promise.resolve (cb()). Then (()=>{throw reason})})} reject) => { reject(1) }).finally(() => { return new Promise((resolve, reject) => { resolve(2) }) }).then(r => { console.log('r: ', r) }, y => { console.log('y: ', y) })Copy the code
  • Catch implementation
Prototype. Catch = function(cb) {return this. Then (null, cb)} Reject) = > {reject (1)}). The catch (() = > {the console. The log (' catch ')}) / / equivalent to the - > new Promise ((resolve, reject) => { reject(1) }).then(null, () => { console.log('catch') })Copy the code
  • Delay object
  Promise.defer = Promise.deferred = function () {
      let dfd = {};
      dfd.promise = new Promise((resolve, reject) => {
          dfd.resolve = resolve;
          dfd.reject = reject;
      });
      return dfd;
  }
Copy the code