Take you to write a complete Promise in three minutes

Take you to write a complete Promise in three minutes

Implement a foundation

Promise is a class that accepts a function parameter, and then has a status variable and then method inside. The initial value of the status state is pending. The callback function of the then method is temporarily stored in memory. After the asynchronous operation of the parameter function that needs to be executed in advance is executed, the successful execution will resolve(value), the status will change to full, and the failed execution will reject (ERR), The status changes to rejected

Based on the above analysis, you can draw out the code:

class MyPromise {
  constructor(fn) {
    this.status = 'pending'
    this.value = null
    this.err = null
    this.fulfilledCallback = null
    this.rejectedCallback = null
    const resolve = value => {
      this.status = 'fulfilled'
      this.value = value
      this.fulfilledCallback(value)
    }
    const reject = err => {
      this.status = 'rejected'
      this.err = err
      this.rejectedCallback(err)
    }
    try {
      fn(resolve, reject)
    } catch (err) {
      reject(err)
    }
  }
  then(success, fail) {
    this.fulfilledCallback = success
    this.rejectedCallback = fail
  }
}

A basic Promise operation is implemented. new MyPromise can pass in an asynchronous function, and then the asynchronous operation of. Then

new MyPromise((resolve, reject) => { 
  setTimeout(() => {
    console.log(12)
    resolve(34) 
  }) 
}).then(rsp => { 
  console.log(rsp) 
})
// Output 12 34

But another. then will report an error

Solve the error reporting of chain operation

However, there is only one then function in this implementation. The real promise can be followed by many then functions. On this basis, we can rewrite the then method to return this object

...
then(success, fail) {
  if (this.status === 'fulfilled') {
    success(this.value)
  }
  if (this.status === 'rejected') {
    fail(this.err)
  }
  this.fulfilledCallback = success
  this.rejectedCallback = fail
  return this
}

Upgrade success event functions and failure event functions to queues

class MyPromise {
  constructor(fn) {
    this.status = 'pending'
    this.fulfilledCallbacks = []
    this.rejectedCallbacks = []
    const resolve = value => {
      this.fulfilledCallbacks.forEach(cb=>{
        cb(value)
      })
    }
    const reject = err => {
      this.rejectedCallbacks.forEach(cb=>{
        cb(err)
      })
    }
    try {
      fn(resolve, reject)
    } catch (err) {
      reject(err)
    }
  }
  then(success, fail) {
    if (this.status === 'fulfilled') {
      this.value = success(this.value)
    } else if (this.status === 'rejected') {
      this.err = fail(this.err)
    } else {
      this.fulfilledCallbacks.push(success)
      this.rejectedCallbacks.push(fail)
    }
    return this
  }
}

new MyPromise((resolve, reject) => {
  setTimeout(() => {
    console.log(12)
    resolve(34)
  })
}).then(rsp => {
  console.log(rsp)
}).then(rsp => {
  console.log(rsp)
})
// 12 34 34

In this way, the callback functions in. then can be executed

The argument of the parameter function in the next then is the return value of the previous parameter function

Promise can not only realize asynchronous, but also complete chain operation. It can also take the return value of the previous parameter function in then as the argument of the parameter function in then

new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(1)
  })
}).then(rsp => {
  console.log(rsp)
  return 2
}).then(rsp => {
  console.log(rsp)
})
// 1 2

Let's also implement it. My idea is to assign the return value of the forEach loop function to this.value after resolve or rejected. Let's try it

...
const resolve = value => {
  this.value = value
  this.fulfilledCallbacks.forEach(cb=>{
    this.value = cb(this.value)
  })
}

new MyPromise((resolve, reject) => {
  setTimeout(() => {
    resolve(1)
  })
}).then(rsp => {
  console.log(rsp)
  return 2
}).then(rsp => {
  console.log(rsp)
})
// 1 2

complete

Implement Promise.all

Don't say much, just go to the code

static all(list) {
  const results =[]
  return new MyPromise((resolve, reject) => {
    list.forEach(promise => {
      promise.then(rsp => {
        results.push(rsp)
        if (results.length === list.length) {
          resolve(results)
        }
      })
    })
  })
}

It can be seen from the above that when the promise in the queue executes one, it will push into the result queue. When the last resolve (the last resolve is not necessarily the last of the queue); The results are all pushed in (results.length === list.length), and the final return is the list of resolve results.

let p1 = new MyPromise((resolve)=> resolve(1))
let p2 = new MyPromise((resolve)=> {
  setTimeout(()=>{
    resolve(2)
  }, 1000)
})
let p3 = new MyPromise((resolve)=> {
  setTimeout(()=>{
    resolve(3)
  })
})

MyPromise.all([p1, p2, p3]).then(rsp=> {
  console.log(rsp)
})

// [1,3,2]

Implement Promise.resolve

The implementation of Promise.resolve is relatively simple

...
static resolve(value) {
  return new MyPromise(resolve=>{
    resolve(value)
  })
}

After testing, there is no problem

MyPromise.resolve(1).then(rsp => {
  console.log(rsp)
})

// 1

Complete code

The whole code is as follows:

class MyPromise {
  constructor(fn) {
    this.status = 'pending'
    this.fulfilledCallbacks = []
    this.rejectedCallbacks = []
    const resolve = value => {
      this.status = 'fulfilled'
      this.value = value
      this.fulfilledCallbacks.forEach(cb=>{
        this.value = cb(this.value)
      })
    }
    const reject = err => {
      this.status = 'rejected'
      this.err = err
      this.rejectedCallbacks.forEach(cb=>{
        this.value = cb(this.err)
      })
    }
    try {
      fn(resolve, reject)
    } catch (err) {
      reject(err)
    }
  }
  then(success, fail) {
    if (this.status === 'fulfilled') {
      this.value = success(this.value)
    } else if (this.status === 'rejected') {
      this.err = fail(this.err)
    } else {
      this.fulfilledCallbacks.push(success)
      this.rejectedCallbacks.push(fail)
    }
    return this
  }
  static all(list) {
    const results =[]
    return new MyPromise((resolve, reject) => {
      list.forEach(promise => {
        promise.then(rsp => {
          results.push(rsp)
          if (results.length === list.length) {
            resolve(results)
          }
        })
      })
    })
  }
  static resolve(value) {
    return new MyPromise(resolve=>{
      resolve(value)
    })
  }
}

Summary and discussion

We only wrote some functions that Promise uses relatively more, and there is a micro task simulation that has not been implemented. As far as we know, the resolve function is asynchronous when it is executed. The implementation principle is similar to node.js process.nextTick. This involves the bottom layer, which we will not discuss here

Keywords: Javascript node.js Front-end

Added by TheRealPenguin on Tue, 26 Oct 2021 17:43:34 +0300