If you don’t know about promises, it’s recommended to read the Use of Promises section first

emphasis

  • The executor function executes immediately
  • State (pending -> depressing or pending -> Rejected)
  • Alter state immediately after execution and execute callback asynchronously (MutationObserver -> setTimeout)
  • .then returns a new Promise and records the resove/ Reject callback of the previous one
  • The return value of onFulfilled/onRejected will be the parameter of the successful callback of the next Promise without errors

implementation

// State constant
// State constant
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

// The implementation of microtasks
const { instanceQueve, timerFunc } = (() = > {
    let instanceQueve = []
    let counter = 1
    const observer = new MutationObserver(() = > {
        const tempQueve = instanceQueve.slice().reverse()
        let len = tempQueve.length
        while(len--) {
            const instance = tempQueve[len]
            if(instance.status == FULFILLED) {
                instance.onFulfilled.forEach(cb= > cb())
            } else {
                instance.onRejected.forEach(cb= > cb())
            }
            instanceQueve.shift()
        }
    })
    const textNode = document.createTextNode(String(counter))
    observer.observe(textNode, {
        characterData: true
    })
    const timerFunc = () = > {
        counter = (counter + 1) % 2
        textNode.data = String(counter)
    }

    return {
        instanceQueve,
        timerFunc
    }
})()

// Decide which task to use
// Try using microtasks first, demote to macro tasks if not supported
// See vue source code for nextTick implementation
// https://developer.mozilla.org/zh-CN/docs/Web/API/MutationObserver
// https://developer.mozilla.org/zh-CN/docs/Web/API/MutationObserver/observe
// https://developer.mozilla.org/zh-CN/docs/Web/API/MutationObserverInit
function decisionTask() {
    if( MutationObserver ! = ='undefined' || (
        MutationObserver.toString() === '[object MutationObserverConstructor]')
    ) {
        timerFunc()
        instanceQueve.push(this)
        return
    } 
    
    setTimeout(() = > {
        // Trigger the callback
        if(this.status == FULFILLED) {
            this.onFulfilled.forEach(cb= > cb())
        } else {
            this.onRejected.forEach(cb= > cb())
        }
    }, 0)}/ / Promise
function LPromise(executor) {
    this.status = PENDING   // Current status
    this.value = undefined  / / the current value
    this.onFulfilled = []    // The cache callback succeeded
    this.onRejected = []     // Cache failed callback
    this.resolveFn = body= > {  / / resolve function
        // Triggered only when the current status is PEDding
        if (this.status ! == PENDING)return

        this.status = FULFILLED
        this.value = body
        
        decisionTask.call(this)}this.rejectFn = error= > {  / / reject function
        if (this.status ! == PENDING)return

        this.status = REJECTED
        this.value = error
        
        decisionTask.call(this)}Execute the executor function immediately
    // Try catch is used to prevent exceptions in executor contents
    try {
        executor(this.resolveFn, this.rejectFn)
    } catch (e) {
        this.rejectFn(e)
    }
}

/ / then method
// This is a big pity. // This is a big pity. // This is a big pity.
LPromise.prototype.then = function (onFulfilled, onRejected) {
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : () = > { }
    onRejected = typeof onRejected === 'function' ? onRejected : () = >{}// chain call
    const _promise = new LPromise((resolve, reject) = > {
        // Cache the onFulfilled and onRejected callback functions passed by the user
        Resolve /reject is invoked when the user is active
        this.onFulfilled.push(() = > {
            // Returns the result of the current callback
            Return to the success callback of the next Promise
            try {
                const result = onFulfilled(this.value)
                resolve(result)
            } catch (e) {
                reject(e)
            }

        })

        this.onRejected.push(() = > {
            // Returns the result of the current callback
            Return to the success callback of the next Promise
            try {
                const result = onRejected(this.value)
                resolve(this.value)
            } catch (e) {
                reject(e)
            }
        })
    })

    return _promise
}

// Catch an error
// Accept a callback (onRejected)
LPromise.prototype.catch = function (onRejected) {
    this.then(null, onRejected)
}

LPromise.resolve = function (value) {
    return new LPromise((resolve, reject) = > {
        resolve(value)
    })
}

LPromise.reject = function (value) {
    return new LPromise((resolve, reject) = > {
        reject(value)
    })
}

Copy the code

test

console.log('script start')

LPromise.resolve(1).then(res= > console.log(res)).then(res= > console.log(res))
p = LPromise.resolve(2)
p.then(res= > console.log(res))
p.then(res= > console.log(res))
const lp1 = new LPromise((resolve, reject) = > {
    a
})

lp1.then(body= >{},error= > {
    console.log(error, 'err')})const lp2 = new LPromise((resolve, reject) = > {
    setTimeout(() = > {
        resolve('sleep 1000ms')},1000)
})

lp2.then(body= > {
    console.log(body)
    return 1
}, null)

const lp3 = new LPromise((resolve, reject) = > {
    setTimeout(() = > {
        reject('Failed')},1000)
})

lp3.then()
lp3.catch(fail= > console.log(fail, ' lp3 err'))

const lp4 = new LPromise((resolve, reject) = > {
    setTimeout(() = > {
        resolve(1)},1)
})

lp4
    .then(body= > {
        console.log(body)
        return 'hop'
    }, null)
    .then(body= > {
        console.log(body)
        return 1
    }, null)

const lp5 = new LPromise((resolve, reject) = > {
    setTimeout(() = > {
        resolve('Test then throw exception inside')},10)
})

lp5
    .then(body= > {
        console.log(body)
        console.log(b)

        return 'hop'
    }, null)
    .then(null.error= > {
        console.log('The last then threw an exception, and I received it! ')
        console.log(error)
    })

// It doesn't make sense because we use macro task emulation
LPromise.resolve(setTimeout(() = > console.log('setTimeout'), 1000))
    .then(body= > console.log(body, 'Promise.resolve'))
LPromise.reject(setTimeout(() = > console.log('setTimeout'), 1000))
    .then(null.body= > console.log(body, 'Promise.reject'))

console.log('script end')  
Copy the code

The test results

> script start > script end >1
> 2
> 2
> ReferenceError: a is not defined
    at <anonymous>:157:5
    at new LPromise (<anonymous>:88:9)
    at <anonymous>:156:13 "err"
> 125 "Promise.resolve"
> 126 "Promise.reject"
> undefined
> 1> hip hop > test then inside throw exception > last then inside throw exception, I received oh!! >ReferenceError: b is not defined
    at <anonymous>:209:21
    at <anonymous>:108:32
    at <anonymous>:16:52
    at Array.forEach (<anonymous>)
    at MutationObserver.<anonymous> (<anonymous>:16:38) > sleep 1000ms > failed lP3 err >setTimeout
> setTimeout
Copy the code

[Notes are not easy, if it is helpful to you, please like, thank you]