Promise和异步机制

异步机制

JavaScript的异步机制是事件轮询

  • Js引擎发起的任务是微任务
  • 宿主(浏览器)发起的任务是宏任务

下面这张图大概阐述了哪些是宏任务哪些是微任务,都是我们比较常用的。比如,promise是微任务,setTimeOut是宏任务。
Promise和异步机制_第1张图片

  • 所有同步任务都先在操作系统主线程上先执行
  • 主线程执行一个宏任务后,先访问微任务队列
  • 微任务队列空了再访问下一个宏任务
  • 下一个宏任务被放到主线程上执行,回到第二点

所以,宏任务需要多次事件循环才能执行完,微任务是一次性执行完的
Promise和异步机制_第2张图片
有一些需要注意的:

  • resolve或者reject改变状态的时候,才会将then或者catch的回调加入微任务队列
  • promise回调(不是then或者catch回调)是同步的,resolve函数的调用也是同步的,可以继续执行resolve之后的代码。
  • 链式调用,只有当上个微任务执行之后才会将下一个链式调用加入微任务队列。所以如果有多个链式调用的情况就要小心了。
  • await虽然是同步代码,会执行。但是await后面的代码会被阻塞,相当于await后面的代码被加入到一个Promise.then()中(相当于此时新建了一个微任务了)
  • async定义function的时候不会被执行,相当于普通函数被定义了。只用调用这个函数才会执行。
    async function async1() {
      console.log("async1 start");
      await async2();
      console.log("async1 end");
    }
    async function async2() {
      console.log("async2");
    }
    async1();
    console.log('start')
    // 'async1 start'
    // 'async2'
    // 'start'
    // 'async1 end'
    

Promise

推荐一篇好文章:一口气刷完45道promise面试题

Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。
所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。
有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)

关于状态

  • Promise的回调是同步的,而then的回调是异步的
  • Promise不返回真实结果,而返回一个承诺,由then来执行回调。reslove、reject来改变状态。
  • Promise的状态一旦发生改变就无法更改(resolve或者reject)
  • 如果是pending状态,then、catch不会执行他们注册的回调,除非状态改变

关于then和catch

  • then的回调里return一个promise会进入等待状态,直到return的Promise改变

  • Promise的then方法的参数期望是函数,传入非函数则会发生值穿透(寻找下一个传值正常的then)

  • 链式调用then,上一个then的返回值是下一个then接受的参数;如果返回错误,会返回一个状态为reject的Promise

  • then会默认return一个Promise,返回结果会被下一个then接收,所以可以链式调用。如果抛出错误,就直接被catch接收。如果手动返回一个Promise,就等待这个Promise状态发生改变,然后继续执行下一个then。

  • catch返回的结果,可以继续被then接受,继续执行后面的then,但是状态依然是reject

  • .then 或者 .catch 中 return 一个 error 对象并不会抛出错误,所以不会被后续的 .catch 捕获。在Promise中,返回任意一个非 promise 的值都会被包裹成 promise 对象,例如return 2会被包装为return Promise.resolve(2)。

    要抛出错误,并且被catch,可以这样写:

    return Promise.reject( new Error('error!!!'));
    // or
    throw new Error('error!!!')
    
  • .then 或 .catch 返回的值不能是 promise 本身,否则会造成死循环

  • 看看下面这种情况:

    Promise.resolve()
     .then(function success (res) {
       throw new Error('error!!!')
     }, function fail1 (err) {
       console.log('fail1', err)
     }).catch(function fail2 (err) {
       console.log('fail2', err)
     })
    

    then的第一个参数用来处理成功的情况,第二个用来处理失败。虽然then的抛出了错误,但是不会被第二个参数的fail1给接收,而是会掉到catch里面的fail2

关于finally

  • .finally()方法不管Promise对象最后的状态如何都会执行
  • .finally()方法的回调函数不接受任何的参数,也就是说你在.finally()函数中是没法知道Promise最终的状态是resolved还是rejected的
  • 它最终返回的默认会是一个原来的Promise对象值(即使你手动返回一个新的值),不过如果抛出的是一个异常则返回异常的Promise对象。

关于all和race

  • .all()的作用是接收一组异步任务,然后并行执行异步任务,并且在所有异步操作执行完后才执行回调。

  • all并行执行任务后,再调用then会返回一组所有结果的集合(数组),这个集合的顺序和Promise.all()接收到的数组顺序一致

  • .race()的作用也是接收一组异步任务,然后并行执行异步任务,只保留取第一个执行完成(最先完成的那个)的异步操作的结果,其他的方法仍在执行,不过执行结果不会被接收,

  • 也就是说,我们试图打印所有结果,使用race之后接下来调用then,会打印各任务执行结果+最先执行的结果(只有这个被接收了),不过打印的顺序和all有所不同,看下面的代码。

    function runAsync (x) {
      const p = new Promise(r => setTimeout(() => r(x, console.log(x)), 1000))
      return p
    }
    Promise.race([runAsync(1), runAsync(2), runAsync(3)])
      .then(res =>console.log('result: ', res))
      .catch(err =>console.log(err))
    // 1
    // result:1
    // 2
    // 3
    

手写Promise

实现所需:status状态量,回调队列,value,resolve函数,reject函数

// 三个常量用于表示状态
const PENDING = 'pending'
const RESOLVED = 'resolved'
const REJECTED = 'rejected'

function MyPromise(fn) {
    const that = this
    this.state = PENDING

    // value 变量用于保存 resolve 或者 reject 中传入的值
    this.value = null

    // 用于保存 then 中的回调,因为当执行完 Promise 时状态可能还是等待中,这时候应该把 then 中的回调保存起来用于状态改变时使用
    that.resolvedCallbacks = []
    that.rejectedCallbacks = []


    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))
        }
    }

    // 完成以上两个函数以后,我们就该实现如何执行 Promise 中传入的函数了
    try {
        fn(resolve,reject)
    }cach(e){
        reject(e)
    }
}

// 最后我们来实现较为复杂的 then 函数
MyPromise.prototype.then = function(onFulfilled,onRejected){
  const that = this

  // 判断两个参数是否为函数类型,因为这两个参数是可选参数
  onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : v=>v
  onRejected = typeof onRejected === 'function' ? onRejected : e=>throw e

  // 当状态不是等待态时,就去执行相对应的函数。如果状态是等待态的话,就往回调函数中 push 函数
  if(this.state === PENDING) {
      this.resolvedCallbacks.push(onFulfilled)
      this.rejectedCallbacks.push(onRejected)
  }
  if(this.state === RESOLVED) {
      onFulfilled(that.value)
  }
  if(this.state === REJECTED) {
      onRejected(that.value)
  }
}

你可能感兴趣的:(JavaScript)