Handwritten promise idea

Handwritten promise ideas

    • Promise usage
    • Handwriting Promise Ideas
    • Handwritten Promise code (abbreviated version)
    • async…await

Promise usage

Why use promises?
The execution result of asynchronous code cannot be obtained through the return value. The return value can only be used to obtain the execution result of synchronous code. Asynchronous code usually requires a callback function as a parameter. When the asynchronous code is executed and the result is obtained, the result can be used as the callback function. The parameter is passed so that we can read the result in the callback function. code show as below:

function sum(a, b){<!-- -->
    setTimeout(()=>{<!-- -->
        return a + b
    }, 10000)
}
let result = sum(123, 456)
console.log(result) //undefined


function sum(a, b, cb){<!-- -->
    setTimeout(()=>{<!-- -->
        cb(a + b)
    }, 10000)
}
sum(123, 456, result => {<!-- -->
    console.log(result) //579
})


function sum(a, b, cb){<!-- -->
    setTimeout(()=>{<!-- -->
        cb(a + b)
    }, 10000)
}
sum(123, 456, result => {<!-- -->
    sum(result, 789, result => {<!-- -->
        sum(result, 10, result=>{<!-- -->
        console. log(result)
        })
    })
})

Through the code, we can see that when there are too many callback functions, callback hell is formed, and promise is specially designed to solve the problem of asynchronous function callback hell.

Creating Promises
const promise = new Promise(executor)
When creating, you need to pass in an executor as a parameter. The executor is a callback function with two parameters, as follows
const promise = new Promise((resolve, reject) => {})
These two parameters are both functions, resolve is called when it succeeds, and reject is called when it fails. They are used to store values in the promise. When using them, they are called according to different situations. The value stored in Promise after 10 seconds is as follows”haha “:

const promise = new Promise((resolve, reject) => {<!-- -->
  setTimeout(() => {<!-- -->
    resolve("haha")
  }, 10000)
})

Use the .then() and .catch() methods to get values from Promise. Then and catch are instance methods of Promise, through which the data stored in Promise can be obtained. , then accepts two callback functions as parameters, the first parameter is used to obtain the value of success (the value stored in resolve()), and the second parameter is used to obtain the value of failure (reject () stored value); catch receives a callback function as a parameter to obtain the value when it fails (the value stored in reject()). then, after the catch is executed, a new Promise will always be returned each time, and the return value of the callback function in then will be stored in this Promise. If no return value is specified, no value will be stored in the new Promise.
When the code execution in Promise fails (or when reject is executed), if we call catch to process data, Promise will pass the error information to the callback function of catch, and we can handle the exception in catch, and at the same time The return value of the catch callback function will be passed down as the data in the next Promise. If we call then to process the data without passing the second parameter, then will not be executed at this time, but the error information will be directly added to the Promise returned in the next step, which will be processed by the subsequent method. In subsequent calls, if there is a second parameter of catch or then, it will be processed normally. If not, report an error. When the Promise is executed normally, if a catch is encountered, the catch will not be executed. At this time, the result in the Promise will be automatically passed to the next Promise for subsequent use.

const p = new Promise((resolve,reject)=>{<!-- -->
  resolve('hahaha')
})
console. log(p);


The console prints Promise, and you can see that there are two attributes: PromiseState and PromiseResult. In Promise, whether it is through resolve, reject or error reporting, the exception information will be stored in PromiseResult; PromiseState is used to indicate the state of the value in Promise. Promise has three states: pending, fulfilled, rejected. pending is the waiting state of Promise. At this time, there is no value in Promise, and the value of PromiseResult is undefined. fulfilled is the successful state of Promise, at this time the value has been stored in Promise normally (through resolve). rejected means rejection, which means that the value is stored through reject, or an error occurred during execution.
Note: The state of Promise is irreversible, and can only be changed once, and can only change from pending to fulfilled or rejected

Finally
The instance method of Promise is different from then and catch in that the callback function in finally will always be executed no matter what the situation is, and the callback function will not receive any parameters, and the return value of finally will not become the next step in Promise result. Simply put, finally is just writing some code that must be executed, and it will not have any real impact on Promise.

const p = new Promise((resolve,reject)=>{<!-- -->
   resolve('hahaha')
})
p.finally(()=>{<!-- -->
   console.log('Always execute') //No parameters, and always execute
})

static method
Promise. all()
all requires an array (iterable object) as a parameter, and multiple promises can be stored in the array. After calling, the all method will return a new Promise, this Promise will be fulfilled after all the Promises in the array are fulfilled, and the results of all Promises will be returned. When we have multiple Promises that need to be executed, and we need multiple Promises to be executed, we can use Promise.all to help us complete this work when processing their results in a unified manner. Simply put, all asynchronous operations are completed before the results are returned.
Promise. allSettled()
all returns valid data only when all Promises are fulfilled, and allSettled is used in the same way as all, but it will return data no matter whether the Promise is fulfilled or not, but it will return different data according to different states.
Success: {status:”fulfilled”, value:result}
Failed: {status: “rejected”, reason: error}
Promise. race()
race will return the first fulfilled Promise and ignore other unfulfilled Promises.
Promise. any()
any is similar to race, but it will only return the first Promise that succeeds, and will return an error message if all Promises fail.
Promise. resolve()
Create a new Promise instance and store a data directly through resolve.
Promise. reject()
Create a new Promise instance and store a data directly through reject.

Handwritten Promise ideas

1. Define a variable to store the value of the Promise.
2. Define the three states of Promise.
3. Promise needs to pass in an executor, which is a callback function with two parameters, resolve and reject, both of which are a function.
4.then is an instance method that returns a Promise. Then has two parameters, both of which are callback functions. The first parameter is called when the status becomes successful, and the second parameter is called when the status is rejected (the second parameter can be optional), the code in then should be added to the microtask queue.
5.catch is an instance method that returns a Promise, has a callback function as a parameter, and is called when the status is rejected.
6. Realize the problem of whether the value passed to Promise asynchronously can be received (the callback function in then is called in resolve to solve the asynchronous problem).
7. Implement chain calls (the return value of the callback function in then is used as the value of the new Promise).
8. The status is irreversible, it can only change from waiting to success or rejection. You can only pass one value to the promise, and multiple passes will not take effect.

Handwritten Promise code (abbreviated version)

// Create 3 states
const PROMISE_STATE = {<!-- -->
  PENDING: 0,
  FULFILLED: 1,
  REJECTED: 2
}
class myPromise{<!-- -->
  // Create a variable to store the result of the Promise
  #result
  // Create a variable to record the state of the Promise
  #state = PROMISE_STATE. PENDING
  // Create a variable to store the callback function in then, then can be called multiple times, use an array to store each callback function
  #callback = []
  constructor(executor){<!-- -->
    // Receive an executor as a parameter
    executor(this.#resolve.bind(this),this.#reject.bind(this))
  }
  // resolve and reject are called internally, so use private methods
  #resolve(value){<!-- -->
    // Forbid the value to be modified repeatedly
    // If state is not equal to 0, it means the value has been modified and the function returns directly
    if(this.#state !== PROMISE_STATE.PENDING) return
    this.#result = value
    this.#state = PROMISE_STATE.FULFILLED
    // When resolve is executed, it means that the data has come in, and the callback function in then is called here
    // Add the synchronization code to the microtask queue
    queueMicrotask(()=>{<!-- -->
      this.#callback.forEach(cb=>{<!-- -->
        // Traverse each callback function of then and call it
        cb()
      })
    })
  }
  #reject(reason){<!-- -->
    if(this.#state !== PROMISE_STATE.PENDING) return
    this.#result = reason
    this.#state = PROMISE_STATE.REJECTED
  }
  then(onFulfilled,onRejected){<!-- -->
    // chain calls
    return new myPromise((resolve,reject)=>{<!-- -->
      // If the status is fulfilled, execute the first function parameter, and then judge whether it is rejected
      if(this.#state === PROMISE_STATE.FULFILLED){<!-- -->
        //Then callback function should be put into the microtask queue for execution instead of calling directly
        queueMicrotask(()=>{<!-- -->
          // The return value of the callback function is used as the value of the new promise
          resolve(onFulfilled(this. #result))
        })
      }else if(this.#state === PROMISE_STATE.REJECTED){<!-- -->
        queueMicrotask(()=>{<!-- -->
          resolve(onRejected(this. #result))
        })
      }else{<!-- -->
        // The status is pending, indicating that resolve is called asynchronously or not
        // When calling asynchronously, pass the callback function to resolve, and you can get the data after resolve is executed
        // call then multiple times
        this.#callback.push(()=>{<!-- -->
          // The callback function is called which is equivalent to onFulfilled being called
          resolve(onFulfilled(this. #result))
        })
      }
    })
  }
  // Define the static method all()
  static all(arr) {<!-- -->
    // Declare an array to store the results of all operations in arr
    let results = [];
    // Declare a variable index to determine whether all asynchronous operations have been completed. If the asynchronous operation is not completed, the index value is less than arr.length
    let index = 0;
    return new MyPromise((resolve, reject) => {<!-- -->
      // Declare the addData function to store the result of the operation into the results array
      function addData(key, value) {<!-- -->
        results[key] = value;
        index + + ;
        if(index === arr. length) {<!-- -->
          resolve(results);
        }
      }
      for(let i = 0; i < arr. length; i ++ ) {<!-- -->
        let current = arr[i];
        if(current instanceof MyPromise) {<!-- -->
          // The current arr element is a Promise object
          current.then((value) => {<!-- -->
            addData(value);
          }, (err) => {<!-- -->
            reject(err);
          })
        } else {<!-- -->
          // The current arr element is not a Promise object
          addData(i, arr[i]);
        }
      }
    })
  }
}

async…await

async…await writes asynchronous code in a synchronous way

An asynchronous function can be created through async, and the return value of the asynchronous function will be automatically encapsulated into a Promise to return

//The following two functions are the same
function fn() {<!-- -->
  return Promise. resolve(10)
}
async function fn2() {<!-- -->
    return 10
}
  • The await keyword can be used in the asynchronous function declared by async to call the asynchronous function. When we call the asynchronous function through await, it will suspend the running of the code, and the result will not be returned until the asynchronous code execution has a result.
  • await can only be used in async functions declared by async, or in the top-level scope of es modules
  • Synchronous writing will block, but await only blocks the code inside the asynchronous function, and will not affect external code
  • When calling asynchronous code through await, you need to handle exceptions through try-catch
  • When we use await to call a function, all the code behind the current function will be put into the microtask team after the current function is executed
async function fn3() {<!-- -->
    // sum(123, 456)
    // .then(r => sum(r, 8))
    // .then(r => sum(r, 9))
    // .then(r => console.log(r))
    try {<!-- -->
        let result = await sum(123, 456)
        result = await sum(result, 8)
        result = await sum(result, 9)
        console. log(result)
    } catch (e) {<!-- -->
        console.log("Something went wrong~~")
    }
 
}