preface

  • Promise specification
  • The realization principle of promise is mainly aimed at then method

1. Copy the cat

  • 1.PromiseClasses don’t have to worry about compatibility
  • 2. Whennew PromiseIs passed an actuatorexecutorThis actuator is executed immediately
  • 3. The currentexecutorGiven two function arguments (resolve.reject) describes the state of the current promise
  • 4.promiseThere are three states waiting for success or failure
  • Default is wait
  • If the callresolveWill lead to success
  • If the callrejectOr the operation fails if an exception occurs
  • 5. EachpromiseEach instance has onethenmethods
  • 6.promiseOnce the state changes, it cannot be changed
let Promise = require('./promise.js')

let promise = new Promise((resolve, reject) = > {
    resolve('ok')
})

promise.then((value) = >{
  console.log("🚀 ~ success1." ", value)
}, (reason) = >{
  console.log("🚀 ~ error1", reason)
})

promise.then((value) = >{
  console.log("🚀 ~ success2", value)
}, (reason) = >{
  console.log("🚀 ~ error2." ", reason)
})

Copy the code
/ * * *@description       Three states *@const PENDING wait *@const FULFILLED successfully *@const REJECTED * / failure
const PENDING = 'PENDING';
const FULFILLED = 'FULFILLED';
const REJECTED = 'REJECTED';

class Promise {
  constructor(executor) {
    this.status = PENDING;    // Status Default wait
    this.value = undefined;   // Success value
    this.reason = undefined;  // Failed value

    const resolve = (value) = > {
      if (this.status === PENDING) {
        this.value = value
        this.status = FULFILLED
      }
    }
    
    const reject = (reason) = > {
      if (this.status === PENDING) {
        this.reason = reason
        this.status = REJECTED
      }
    }

    try {
      // The actuator passed in is executed immediately by default
      executor(resolve, reject)
    } catch (error) {
      reject(error)
    }

  }

  then(onFulfilled, onRejected) {
    // Successful callback
    if (this.status === FULFILLED) {
      onFulfilled(this.value)
    }

    // Failed callback
    if (this.status === REJECTED) {
      onRejected(this.reason)
    }
  }

}

module.exports = Promise

Copy the code

2. Asynchronous processing

  • Publish and subscribe model
  • How to handle asynchrony
  • If you havesetTimeoutIs executed only after the current context finishes executingresolve()
let promise = new Promise((resolve, reject) = > {
  setTimeout(() = >{
    resolve('ok')},3000)
})

promise.then((value) = >{
  console.log("🚀 ~ success1." ", value)
}, (reason) = >{
  console.log("🚀 ~ error1", reason)
})

promise.then((value) = >{
  console.log("🚀 ~ success2", value)
}, (reason) = >{
  console.log("🚀 ~ error2." ", reason)
})
Copy the code
/ * * *@description       Three states *@const PENDING wait *@const FULFILLED successfully *@const REJECTED * / failure
const PENDING = 'PENDING';
const FULFILLED = 'FULFILLED';
const REJECTED = 'REJECTED';

class Promise {
    constructor(executor) {
        this.status = PENDING;    // Status Default wait
        this.value = undefined;   // Success value
        this.reason = undefined;  // Failed value

        this.onFulfilledCallbacks = [];   // async callback method when the store succeeds
        this.onRejectedCallbacks  = [];   // async callback method when storage fails
 
        const resolve = (value) = > {
            if (this.status === PENDING) {
                this.value = value
                this.status = FULFILLED

                // Asynchronous problem publishing
                this.onFulfilledCallbacks.forEach(fn= > fn())
            }
        }
    
        const reject = (reason) = > {
            if (this.status === PENDING) {
                this.reason = reason
                this.status = REJECTED

                // Asynchronous problem publishing
                this.onRejectedCallbacks.forEach(fn= > fn())
            }
        }
 
        try {
            // The actuator passed in is executed immediately by default
            executor(resolve, reject)
        } catch (error) {
            reject(error)
        }
 
   }
 
    then(onFulfilled, onRejected) {
        // Subscribe when there is an asynchronous problem
        // The status is PENDING by default
        if (this.status === PENDING) {
            this.onFulfilledCallbacks.push(() = > {
                onFulfilled(this.value)
            })

            this.onRejectedCallbacks.push(() = > {
                onRejected(this.reason)
            })
        }

        // Successful callback
        if (this.status === FULFILLED) {
            onFulfilled(this.value)
        }
    
        // Failed callback
        if (this.status === REJECTED) {
            onRejected(this.reason)
        }
    }
 
}
 
module.exports = Promise
Copy the code

3. Chain call (core)

  • 1. CallthenWill return aThe new promise
  • 2.thenThe method returns a normal value as the result of the success of the next THEN
  • 3.thenError execution of the method in, will be the result of the next then failure
  • 4.thenThe method returns a promise according to the promiseresolve, rejectTo deal with success or failure
  • 5. No matterthenA walk succeeds or fails as long as it returns a normal valuethenThe success of the
let Promise = require('./promise.js')

let promise = new Promise((resolve, reject) = >{ resolve('Chain callback')})// then1
let promise2 = promise.then(
  value= > { return value + '1' },
  error= > { return error + '1'});// The first special case resolvePromise
// let promise2 = promise.then(
// value => { return promise2 },
// error => { return error + '1' }
// );

// The second special case resolvePromise
// let promise2 = promise.then(
// value => {
// // x could be a promise
// return new Promise((resolve,reject)=>{
// setTimeout(() => {
// resolve(new Promise((resolve,reject)=>{
// setTimeout(() => {
/ / resolve (' x is promise ');
/ /}, 1000);
/ /}))
/ /}, 1000)
/ /})
/ /},
// error => { return error + '1' }
// );

// then2
// 🚀 ~ success chain callback 1
promise2.then().then(null).then(
  value= > { console.log("🚀 ~ success", value) },
  error= > { console.log('🚀 ~ failure', error) }
);

// It can also be written like this
// promise.then(
// value => { return value + '1' },
// error => { return error + '1' }
// ).then(
/ / value = > {the console. The log (" 🚀 ~ success ", value)},
. / / the error = > {the console log (' 🚀 ~ failure, the error)}
// );

Copy the code
// chain call
// 1. Resolve nested callback (hell callback)
// 2. Synchronize the concurrency
// 3. Multiple asynchronous processing errors

/ * * *@description       Three states *@const PENDING wait *@const FULFILLED successfully *@const REJECTED * / failure
const PENDING = 'PENDING';
const FULFILLED = 'FULFILLED';
const REJECTED = 'REJECTED';

class Promise {
    constructor(executor) {
        this.status = PENDING;    // Status Default wait
        this.value = undefined;   // Success value
        this.reason = undefined;  // Failed value

        this.onFulfilledCallbacks = [];   // async callback method when the store succeeds
        this.onRejectedCallbacks  = [];   // async callback method when storage fails

        const resolve = (value) = > {
            // If value is a promise, then execute
            if (value instanceof Promise) {
                return value.then(resolve, reject)
            }

            if (this.status === PENDING) {
                this.value = value
                this.status = FULFILLED

                // Asynchronous problem publishing
                this.onFulfilledCallbacks.forEach(fn= > fn())
            }
        }
    
        const reject = (reason) = > {
            if (this.status === PENDING) {
                this.reason = reason
                this.status = REJECTED

                // Asynchronous problem publishing
                this.onRejectedCallbacks.forEach(fn= > fn())
            }
        }

        try {
            // The actuator passed in is executed immediately by default
            executor(resolve, reject)
        } catch (error) {
            reject(error)
        }

}

    then(onFulfilled, onRejected) {
        // Nothing is passed continuously
        // promise.then().then().then(...)
        onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : v= > v;
        onRejected = typeof onRejected === 'function' ? onRejected : error= > { throw error }

        let promise2 = new Promise((resolve, reject) = > {
            // Successful callback
            // Take success as an example:
            Resolve, reject, (try catch)
            // 2. If a promise object is returned, use setTimeout to obtain the current promise2 object
            if (this.status === FULFILLED) {
                setTimeout(() = > {
                    try {
                        let x = onFulfilled(this.value)
                        resolvePromise(promise2, x, resolve, reject)
                    } catch (error) {
                        reject(error)
                    }
                }, 0)}// Failed callback
            if (this.status === REJECTED) {
                setTimeout(() = > {
                    try {
                        let x = onRejected(this.reason)
                        resolvePromise(promise2, x, resolve, reject)
                    } catch (error) {
                        reject(error)
                    }
                }, 0)}// Subscribe when there is an asynchronous problem
            // The status is PENDING by default
            if (this.status === PENDING) {
                this.onFulfilledCallbacks.push(() = > {
                    setTimeout(() = > {
                        try {
                            let x = onFulfilled(this.value)
                            resolvePromise(promise2, x, resolve, reject)
                        } catch (error) {
                            reject(error)
                        }
                    }, 0)})this.onRejectedCallbacks.push(() = > {
                    setTimeout(() = > {
                        try {
                            let x = onRejected(this.reason)
                            resolvePromise(promise2, x, resolve, reject)
                        } catch (error) {
                            reject(error)
                        }
                    }, 0)})}})return promise2
    }

}

/ * * *@description     Process the result * returned by then@param Promise2 then returns a new promise object *@param X then parameter: ondepressing, onFulfilled, undefined * if there is no return@param Resolve Resolve * of promise2@param Reject Reject */ of promise2
function resolvePromise(promise2, x, resolve, reject) {
    // The first special case resolvePromise
    if (promise2 === x) { return reject(new TypeError('TypeError: Chaining cycle detected for Promise #
      
        dead loop '
      ))}if ((typeof x === 'object'&& x ! = =null) | |typeof x === 'function') {
        // Prevent someone else's written promise from failing after a successful call
        let called = false;
        
        try {
            // The second special case resolvePromise
            // It is possible that then is implemented via Object.defineProperty
            let then = x.then;
            
            if (typeof then === 'function') {
                // Then the promise is used to prevent exceptions that trigger getters
                then.call(x, y= > {
                        if (called) return;
                        called = true;

                        // Continue parsing until no promise
                        resolvePromise(promise2, y, resolve, reject)
                    }, r= > {
                        if (called) return;
                        called = true;
                        reject(r);
                    });
            } else {
                {} {then: {}}
                resolve(x)
            }

        } catch (error) {
            if (called) return;
            called = true; reject(error); }}else {
        / / common valuesresolve(x); }}module.exports = Promise
Copy the code

Promise specification testing

  • The installationnpm install promises-aplus-tests -g
  • performpromises-aplus-tests promise.js
  • If there are no errors in the testpromiseWritten in accordance with the specification
  • Only forpromiseIn theresolve, reject
/ / promise. Ts file

// other code....

Promise.deferred = function () {
    let dfd = {};
    dfd.promise = new Promise((resolve,reject) = >{
        dfd.resolve= resolve;
        dfd.reject = reject;
    }); 
    return dfd;
}

module.exports = Promise
Copy the code

Other methods

Promise.resolve, Promise.reject, catch

class Promise {
  // other code ...

  // Promise.resolve
  static resolve(value) {
    return new Promise((resolve, reject) = > {
      resolve(value)
    })
  }

  static reject(value){
    return new Promise((resolve,reject) = >{ reject(value); })}catch(errorFn){
      return this.then(null,errorFn)
  }

}
Copy the code

race

  • Quickest to adoptPromise.race
  • In fact, every promise in the array has been executed
  • The execution of one party is stopped immediately
  • The main fastest has been revisedthis.statusstate
  • Others also perform butIf judgmentskip
class Promise {

  // other code ...

  static race(promises) {
      return new Promise((resolve, reject) = > {
          for (let i = 0; i < promises.length; i++) {
              const p = promises[i];
              if (p && typeof p.then === 'function') {
                  p.then(resolve, reject)
              } else {
                  resolve(p)
              }
          }
      })
  }

}
Copy the code
let Promise = require('./promise.js')

let p1 = new Promise((resolve,reject) = >{
    setTimeout(() = > {
        resolve('success')},1000);
})

let p2 = new Promise((resolve,reject) = >{
    setTimeout(() = > {
        reject('failure')},500);
})

1 / / examples
// 'failed'
Promise.race([p1, p2]).then(
    v= > console.log(v),
    e= > console.log(e)
)

2 / / examples
/ / 1
Promise.race([p1, p2, 1]).then(
    v= > console.log(v),
    e= > console.log(e)
)
Copy the code
// make a timeout interrupt
let p3 = new Promise((resolve) = > {
    setTimeout(() = >{
        resolve('p3 success')},3000)})function warp(proParams) {
    // Export interrupts
    let abort;
    let p = new Promise((resolve, reject) = > {
        abort = reject
    })

    let result = Promise.race([p, proParams])
    result.abort = abort

    return result
}

let p3IsTimeOut = warp(p3)

p3IsTimeOut.then(
    value= > console.log(value),
    error= > console.log(error)
)

// end: 'one second out of time'
setTimeout(() = > {
    p3IsTimeOut.abort('One second out of time')},1000)
Copy the code

finally

  • Execute in failure or success
  • And can continue.then
  • successcb()The returned value is not passed down or.thenthevalue
  • Execution ends on failurecb()And throw an error (the error will continue to drop)
  • Why usePromise.resolve?
  • becausecb()Execute if return valuePromiseobject
  • Direct executionval.then(resovle, reject)And returnThe new promise
class Promise {
  // other code ...

  finally(cb) {
        return this.then(
            value= > {
                return Promise.resolve(cb()).then(() = > value)
            },
            error= > {
                return Promise.resolve(cb()).then(() = > { throw error })
            }
        )
  }

}
Copy the code
let Promise = require('./source/promise3.js')

let p1 = new Promise((resolve,reject) = >{
    setTimeout(() = > {
		    1 / / results
		    resolve('resolve: success')
		    2 / / results
        // reject('reject: fail ')
    }, 3000);
})

let p2 = p1.finally(() = > {
    return new Promise((resolve, reject) = > {
       	setTimeout(() = > {
        	resolve('123456');
       	}, 1000); })})1 / / results
/ / resolve: success
2 / / results
// 🚀 ~ catch reject: fails
p2.then(value= >{
    console.log(value)
}).catch(error= > {
	console.log("🚀 ~ catch", error)
})
Copy the code

In the node promisify

// a.txt this is a test fileCopy the code
const fs = require('fs')

// const { promisify } = require("util");
// const readFile = promisify(fs.readFile)

// This is a test file
// readFile('./a.txt', 'utf8').then(data => {
// console.log("🚀 ~ data", data)
// })


function promisify(fn) {
    return function(. args){
        return new Promise((resolve, reject) = >{ fn(... args,(err, data) = > {
                if(err) returnreject(err); resolve(data); }}})})let p = promisify(fs.readFile)

// This is a test file
p('./a.txt'.'utf8').then(v= > console.log(v))

Copy the code

After the