This is the 30th day of my participation in the Gwen Challenge in November. Check out the details: The Last Gwen Challenge in 2021.”

preface

Before is busy with business development, the basic knowledge of front end to check what are used, rarely has time to sink down and their induction, summarizes the basis, sometimes, what use, feel worthy of pay attention to the thing, are also recorded in his note, also not timely, and then as time goes by, one record everything is at sixes and sevens, I haven’t done it yet. This time, write the basic series of the front end

I plan to chat in a relaxed, chatty tone. If I’m not good at writing, I’ll let you know

Quickly review

  1. Concurrency is different from parallelism
  2. Callback function
  3. Generator
  4. Promise
  5. Async and await
  6. Common timer function

In the previous section, we learned about some of the pitfalls of promises. In this section, we implement a Promise manually

Implement a simplified version of Promise

Let’s first set up the general framework for building functions

const PENDING = 'pending' 
const RESOLVED = 'resolved' 
const REJECTED = 'rejected' 

function MyPromise(fn) { 
    const that = this 
    that.state = PENDING 
    that.value = null 
    that.resolvedCallbacks = [] 
    that.rejectedCallbacks = [] 
    // Resolve and reject functions need to be perfected
    // Fn function to be executed
}
Copy the code
  • First of all, we created three constants to represent the state. Some values that are frequently used should be managed by constants, which is convenient for development and later maintenance
  • The constants are first created inside the function bodythatBecause the code may be executed asynchronously to get the correctthisobject
  • At the beginningPromiseThe state of theta should bepending
  • valueVariables are used to saveresolveorrejectThe value passed in to
  • resolvedCallbacks 和 rejectedCallbacksUsed to holdthenBecause when the execution is donePromiseWhen the state may still be waiting, this time should bethenThe callback is saved for use when the state changes

Next, we’ll refine the resolve and reject functions, adding them inside the MyPromise body

function resolve(value) { 
    if (that.state === PENDING) { 
        that.state = RESOLVED 
        that.value = value 
        that.resolvedCallbacks.map(cb= > cb(that.value)) 
    } 
} 

function reject(value) { 
    if (that.state === PENDING) { 
        that.state = REJECTED 
        that.value = value 
        that.rejectedCallbacks.map(cb= > cb(that.value)) 
    } 
}
Copy the code

These two functions have similar code, so they are parsed together

  • First, both functions have to determine whether the current state is pending, because the specification states that states can be changed only if they are pending
  • Changes the current state to the corresponding state and assigns the value passed tovalue
  • Iterate through the callback array and execute

Now that we’re done with these two functions, how do we implement the functions passed in in Promise

try { 
    fn(resolve, reject)
} catch (e) { 
    reject(e) 
}
Copy the code
  • The implementation is simple, taking the arguments passed in and passing in the previous two functions as arguments
  • Be aware that errors may be encountered during the execution of a function, which needs to be caught and executedrejectfunction

Finally, we implement the more complex THEN function

MyPromise.prototype.then = function(onFulfilled, onRejected) {
  const that = this
  onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : v= > v
  onRejected =
    typeof onRejected === 'function'
      ? onRejected
      : r= > {
          throw r
        }
  if (that.state === PENDING) {
    that.resolvedCallbacks.push(onFulfilled)
    that.rejectedCallbacks.push(onRejected)
  }
  if (that.state === RESOLVED) {
    onFulfilled(that.value)
  }
  if (that.state === REJECTED) {
    onRejected(that.value)
  }
}
Copy the code
  • The first step is to determine whether the two arguments are function types because they are optional
  • If the parameter is not a function type, you need to create a function to assign the value to the parameter, and also implement pass-through, as shown in the following code
// This code currently reports errors in the simple version
// Just as a pass-through example
Promise.resolve(4).then().then((value) = > console.log(value))
Copy the code

What follows is a set of logic to determine the state, and when the state is not a wait state, the corresponding function is executed. If the state is wait-state, call back the push function in the function, such as the following code will enter the wait-state logic

new MyPromise((resolve, reject) = > {
  setTimeout(() = > {
    resolve(1)},0)
}).then(value= > {
  console.log(value)
})
Copy the code

The above is a simple version of the Promise implementation, the next section is to achieve the full version of the Promise of the analysis, I believe that after reading the full version of you, you will understand the Promise to the next level.

Implement A Promise that conforms to the Promise/A+ specification

Recommended reading: Promise/A+ specification

Let’s start with the resolve and reject functions

function resolve(value) { 
    if (value instanceof MyPromise) { 
        return value.then(resolve, reject)
    } 
    setTimeout(() = > { 
        if (that.state === PENDING) { 
            that.state = RESOLVED 
            that.value = value 
            that.resolvedCallbacks.map(cb= > cb(that.value)) 
        } 
    }, 0)}function reject(value) { 
    setTimeout(() = > { 
        if (that.state === PENDING) { 
            that.state = REJECTED 
            that.value = value 
            that.rejectedCallbacks.map(cb= > cb(that.value)) 
        } 
    }, 0)}Copy the code
  • forresolveFunction, we first need to determine whether the value passed isPromisetype
  • To ensure that the function is executed sequentially, use both function body codesetTimeoutwrapped

Next we need to modify the code in the THEN function. First we need to add a variable promise2, because each THEN function returns a new Promise object that holds the new return object. Then we need to modify the logic that determines the wait state

if (that.state === PENDING) { 
    return (promise2 = new MyPromise((resolve, reject) = > { 
        that.resolvedCallbacks.push(() = > { 
            try { 
                const x = onFulfilled(that.value) 
                resolutionProcedure(promise2, x, resolve, reject) 
            } catch (r) { 
                reject(r) 
            } 
        }) 
        that.rejectedCallbacks.push(() = > { 
            try { 
                const x = onRejected(that.value) 
                resolutionProcedure(promise2, x, resolve, reject) 
            } catch (r) { 
                reject(r) 
            } 
        }) 
    })) 
}
Copy the code
  • First we return a new onePromiseObject and inPromiseA function is passed into the
  • The basic logic of this function is the same as before, calling back into an arraypushfunction
  • Again, errors may be encountered during the execution of the function, so it is usedtry... catchThe parcel
  • Specification, implementationonFulfilledoronRejectedThe function returns onexAnd executePromiseSolution process, this is for differentPromiseIs compatible with JQueryPromiseES6 compatiblePromise

Next we modify the logic that determines the state of execution

if (that.state === RESOLVED) { 
    return (promise2 = new MyPromise((resolve, reject) = > { 
        setTimeout(() = > { 
            try { 
                const x = onFulfilled(that.value) 
                resolutionProcedure(promise2, x, resolve, reject) 
            } catch (reason) { 
                reject(reason) 
            } 
        }) 
    })) 
}
Copy the code
  • In fact, you can see that this code is basically the same as the logic to determine the wait state, except that the body of the passed function needs to be executed asynchronously, which is also specified by the specification
  • I’m not going to go over the logic of rejection, but I’ll leave it to you to do the homework

Finally, and of course the hardest part, is implementing the resolutionProcedure function that is compatible with multiple promises

function resolutionProcedure(promise2, x, resolve, reject) { 
    if (promise2 === x) { 
        return reject(new TypeError('Error'))}}Copy the code

First, the specification states that X cannot be equal to promise2, which causes cyclic reference problems, such as the following code

let p = new MyPromise((resolve, reject) = > {
  resolve(1)})let p1 = p.then(value= > {
  return p1
})
Copy the code

And then we need to determine the type of x

if (x instanceof MyPromise) { 
    x.then(function(value) { 
        resolutionProcedure(promise2, value, resolve, reject)
    }, reject) 
}
Copy the code

The code here is implemented in full compliance with the specification. If x is a Promise, you need to determine the following:

  1. ifxIn the waiting state,PromiseMust remain in wait state untilxTo be carried out or rejected
  2. ifxIn any other state, the same value is usedPromise

Of course, these are the cases where the specification requires us to judge, and it is actually possible that we do not judge the state.

Let’s continue to implement the rest of the code in accordance with the specification

let called = false
if(x ! = =null && (typeof x === 'object' || typeof x === 'function')) {
  try {
    let then = x.then
    if (typeof then === 'function') {
      then.call(
        x,
        y= > {
          if (called) return
          called = true
          resolutionProcedure(promise2, y, resolve, reject)
        },
        e= > {
          if (called) return
          called = true
          reject(e)
        }
      )
    } else {
      resolve(x)
    }
  } catch (e) {
    if (called) return
    called = true
    reject(e)
  }
} else {
  resolve(x)
}
Copy the code
  • Start by creating a variablecalledUsed to determine whether a function has been called
  • And then determinexWhether it is an object or a function, if neither, willxThe incomingresolve 中
  • ifxIf it’s an object or a function, let’s firstx.thenAssigned tothenAnd then judgethenType, if not a function typexThe incomingresolve 中
  • ifthenIf it is a function type, it willxAs the scope of the functionthisCall, and pass two callback functions as arguments, the first of which is calledresolvePromiseThe second parameter is calledrejectPromiseBoth callback functions need to determine whether the function has already been executed and then do the corresponding logic
  • If the above code is thrown incorrectly during execution, the error is passed inrejectIn the function

conclusion

  1. Simple version of the Promise
  2. Promises that conform to the Promise/A+ specification

Ps: Dry words or more boring, and hard to impress, we are still in daily use, more experience