I’ve read a lot of promise articles and I’ve written promises, I know how to use them, I know how promise works, but what’s the idea? It wasn’t until I saw functors in functional programming that I found the answer. Promise is the application of functionally programming functors. The purpose of asynchronous schemes is to make asynchronous code write like synchronization, so promise implements synchronous chain calls, which is the idea of functors

To review the concept of functors, click here

1. Functor

class Functor{
    constructor(value) {
        this._value = value
    }
    map(fn) {
        return Functor.of(fn(this._value))
    }    
    static of(value) {
        return new Functor(value)
    }
}    
Copy the code

That’s the functor up there, and the idea is that

  • Each Functor is a new object

  • Object has a map function on its prototype chain

  • The data saved by the functor is processed by the function fn passed in map, and the value obtained is used to generate new functors, so as to realize the value transfer of chain call

2. Realize asynchronous chain call

The above functor is synchronous chain call. If we want asynchronous chain call we need to pass in an executor excutor contains a parameter resolve to delay the asynchronous callback,

1. The first step is to eliminate the idea of functors. We first get the values passed in

class MyPromise{
  constructor(excutor){
    this._value = null
    let resolve = (value) => {
      this._value = value
      console.log(this._value);
    }
    excutor(resolve)
  }
}
new MyPromise((resolve) => {
    setTimeout(()=>{
        resolve('resolve')
    }, 1000)
})
Copy the code

As shown in the figure above, we get the value of resolve passed in from the outside. Note that excutor is passed in from the outside. Resolve is defined internally by the promise and called externally, so we can change the internal _value

2. Once we get the value from the external functor, we need to process the value through the map passed in by fn, but since fn may be asynchronous, we can find a queue to store the value and pass it to the next functor’s resolve

So let’s just write map as then for the sake of comprehension

class MyPromise{ constructor(excutor){ this._value = null this.callback = [] let resolve = (value) => { this._value = value this.callback && this.callback.forEach( item => item()) } excutor(resolve) } then(fn) { return new MyPromise((resolve) => { this.callback.push(() => { let data = fn(this._value) resolve(data) }) }) } } var promise = new  MyPromise( (resolve) => { setTimeout( () => { resolve(1) }, 1000) }) promise.then((res) => { return res + 1 }).then( res => { console.log(res) return res + 1 })Copy the code

Call resolve’s callback through the then method and execute the collected callback in resolve

3. Add failure callbacks and states to MyPromise functors

Sometimes asynchronous callbacks do not get results and need to return an error message, so you need to add an error callback to handle the exception, written the same way as a successful callback

As we know, the initial state of promise is pending, the completion state is fulfilled, and the failure state is rejected. In the successful or failed callback state, the modification state is fulfilled

const PENDING = 'pending' const FULFILED = 'fulfilled' const REJECTED = 'rejected' class MyPromise{ constructor(excutor){ this._value = null this._reason = null this._status = PENDING this.resolveCallback = [] this.rejectCallback = [] let resolve = (value) => { this._value = value this._status = FULFILED this.resolveCallback && this.resolveCallback.forEach( item => item()) } let reject = (reason) => { this._reason = reason this._status = REJECTED  this.rejectCallback && this.rejectCallback.forEach( item => item()) } excutor(resolve, reject) } then(resolveFn, rejectFn) { return new MyPromise((resolve, reject) => { this.resolveCallback.push(() => { let data = resolveFn(this._value) resolve(data) }) this.rejectCallback.push(() => { let data = rejectFn(this._reason) reject(data) }) }) } }Copy the code

4. Add state judgment, return a MyPromise processing method in level 1 THEN

** See some articles write asynchronous processing, using setTimout is actually unreasonable, because setTimeout is a macro task, ** and promise is a micro task, using a macro task to implement a micro task, is inherently unreasonable

So we’re going to use queueMicrotask, which is actually A microtask that we implement with promise, and we’re going to use queueMicrotask just to get past the promise A+

resolve = (value) => { if( this._status == PENDING ) { queueMicrotask(() => { this._value = value this._status = FULFILED this.resolveCallback && this.resolveCallback.forEach( item => item()) }, 0) } } reject = (reason) => { if( this._status == REJECTED ) { queueMicrotask(() => { this._reason = reason this._status  = REJECTED this.rejectCallback && this.rejectCallback.forEach( item => item()) }) } }Copy the code

Here we add _status judgment, only in pending state can modify the state of MyPromise, then add state judgment, only pending phone callback, other states directly execute the corresponding state function

then(resolveFn, rejectFn) {    
  return new MyPromise((resolve, reject) => {      
    if( this._status == PENDING ) {        
      this.resolveCallback.push(() => {          
        let data = resolveFn(this._value)         
        resolve(data)        
      })       
      this.rejectCallback.push(() => {         
        let data = rejectFn(this._reason)          
        reject(data)        
      })      
    }else if( this._status == FULFILED ) {        
      let data = resolveFn(this._value)        
    if( data instanceof MyPromise ) {          
      data.then( resolve, reject)        
    }else{          
      resolve(data)        }      
    }else if( this._status == REJECTED ) {        
      reject(this._reason)      
    }    
  })   
}
Copy the code

So what you see here is that if MyPromise returns a MyPromise object you need to pass the value of that returned MyPromise

if( data instanceof MyPromise ) {
  data.then( resolve, reject)
}
Copy the code

So you have a basic Promise, and of course you have a series of judgments to write, which can be refined later, but this is mainly about understanding the principle of promises

3. Add static and instance methods

Resolve, Promise. Reject, Promise. All, Promise. Race

1.Promise. Resolve, which is actually simple, is to produce a Promise object whose state is Fulfiled

static resolve(parameter) {
  if(parameter instanceof MyPromise) {
    return parameter  }
  return new MyPromise(resolve=>{
resolve(parameter)      })
}
Copy the code

2.Promise.reject

static reject(reason) {
  return new MyPromise((resolve, reject) => {
    reject(reason)
  })
}
Copy the code

3.Promise.all, pass in a Promise object list that returns results for each Promise in the list

static all (promiseList) {
    return new MyPromise((resolve, reject) => {
      const result = [];
      const length = promiseList.length;
      let count = 0;

      if (length === 0) {
        return resolve(result);
      }

      promiseList.forEach((promise, index) => {
        MyPromise.resolve(promise).then((value) => {
          count++;
          result[index] = value;
          if (count === length) {
            resolve(result);
          }
        }, (reason) => {
          reject(reason);
        });
      });
    });

  }
Copy the code

4. Race, as the name implies, is a race. Whichever result in the list returns the fastest is taken

static race (promiseList) { return new MyPromise((resolve, reject) => { const length = promiseList.length; if (length === 0) { return resolve(); } else { for (let i = 0; i < length; i++) { MyPromise.resolve(promiseList[i]).then((value) => { return resolve(value); }, (reason) => { return reject(reason); }); }}}); }Copy the code

Instance methods are mainly catch and finally

1. Promise. catch is to catch an error callback

Catch (onRejected) {this.then(undefined, onRejected); }Copy the code

2. Promise. finally This callback is executed regardless of whether the final state of the promise succeeds or fails

finally (fn) {
    return this.then((value) => {
      return MyPromise.resolve(fn()).then(() => {
        return value;
      });
    }, (error) => {
      return MyPromise.resolve(fn()).then(() => {
        throw error
      });
    });
}
Copy the code

4. Verify and pass the Promise A+ specification test

1. Install the promises – aplus – tests

npm install promises-aplus-tests --save-dev
Copy the code

2. Add test code

MyPromise.deferred = function () {
  var result = {};
  result.promise = new MyPromise(function (resolve, reject) {
    result.resolve = resolve;
    result.reject = reject;
  });

  return result;
}
module.exports = MyPromise;
Copy the code

3. Add running commands to package.json

"Scripts ": {"test": "promises-aplus-tests' file name '"}Copy the code

4. Run NPM run test

If you see this screen after you run it, congratulations on making a Promise

Refer to the link: www.cnblogs.com/chenwenhao/… Juejin. Cn/post / 694531…