学习笔记(二)—— 异步编程

单线程JavaScript

  • 为了避免多线程同步(操作DOM)问题
  • JS执行环境中,负责执行代码的线程只有一个
  • 优点:
    • 安全
    • 简单
  • 缺点:
    • 耗时任务阻塞执行

同步模式

同步代码依次加入调入栈,执行完成后从调用栈移除

异步模式

  • 不会等待任务结束再执行,开启后立即执行下一个任务

  • 后续逻辑通过回调函数定义

  • 使单线程JavaScript能够执行大量耗时任务

  • 缺点:代码执行顺序混乱

  • JavaScript是单线程,但浏览器并不是单线程

异步任务交给异步线程执行,执行完成后,将回调逻辑加入消息队列,当调用栈中的任务执行完成后,事件循环将消息队列中的第一个任务加入调用栈执行,开始下一轮事件循环

Promise

由CommonJS社区提出了Promise的规范,在ES2015中被标准化

  • Promise有三种状态

    • pending
    • fulfilled
      • 调用resolve
    • rejected
      • 调用reject
  • Promise的状态一旦确定后就不能再被修改

  • 使用案例

    • 模拟实现Promise版ajax

      function ajax(url) {
          return new Promise(function(resolve, reject) {
              const xhr = new XMLHttpRequest()
              xhr.open('GET', url)
              xhr.responseType = 'json'
              xhr.onload = function() {
                  if (this.status === 200) {
                      resolve(this.response)
                  } else {
                      reject(new Error(this.statusText))
                  }
              }
              xhr.send()
          })
      }
      
  • 常见错误

    • 嵌套调用
  • 链式调用

    • then方法会返回一个全新的Promise对象
    • then方法为之前返回的Promise对象注册回调
    • 前面then方法中回到函数的的返回值会作为后面then方法回到的参数传入
    • 如果回调中返回的是Promise,那后面的then的回调会等待Promise结束
    • 如果传入then的参数非回调函数,这个then会被忽略执行(跳过)
  • 异常处理

    • 两种方式
      • then方法的第二个参数回调函数中捕获
      • catch方法回调函数中捕获
    • 差异
      • then方法的第二个参数回调函数可以捕获then之前的Promise产生的异常
      • cache方法可以捕获前面Promise以及then中产生的异常,实际为前面then返回的新Promise,只不过异常通过then的链式调用传递了
    • 全局异常捕获
      • web环境window.addEventListener注册unhandledrejection事件
      • node环境process.on注册unHandledRejection事件
      • 不推荐采用全局捕获的方式处理异常,而应该在代码中明确捕获异常并处理
  • 静态方法

    • Promise.resolve()
      • 如果传入的是普通的值,返回一个已经resolve的Promise对象,Promise.reslve(value)等同于new Promise(resolve => resolve(value))
      • 如果传入的是Promise对象,则直接返回传入的Promise对象本身,使用===比较相等
      • 如果传入的是thenable对象(实现了then方法并带有resolve,reject回调函数参数的对象),则转化为相应then方法的Promsie对象,这个特性可以用来转化第三方库实现的Promise对象为标准Promise对象
    • Promise.reject()
      • 无论传入什么值,都会作为reject的原因传入异常捕获回调
  • 并行执行

    • Promise.all()
      • 接收一个Promise对象数组作为参数,返回一个Promise对象
        • 数组中的所有Promise都被resolve后,将所有Promsie对象resolve返回的数组传入then的回调
        • 如果有Promise被reject,则返回第一个被reject的Promise的状态传入then或cache的回调
    • Promise.race()
      • 接收一个Promise对象数组作为参数,数组中的任意Promise状态发生变化,返回一个Promise对象,将第一个发生状态变化的Promise状态传入相应的回调
  • 执行时序

    • Promise的回调作为微任务,其他还有node的process.nextTick,MutationObserver
    • setTimeout、setInterval等作为宏任务

Generator异步方案

ES2015引入,生成器函数,使异步调用操作扁平化

  • 函数名前加*为生成器函数,调用生成起函数返回一个生成器对象

  • 函数中通过yield关键字,可以暂停函数执行

  • 生成器对象通过调用next()方法,执行到下一个yield之前的代码,并返回一个对象,包含value和done两个属性

    • value:yield之后的表达式的值
    • done:表示生成器是是否执行完成
  • 调用next()方法传入参数,则参数值会作为yield的返回值,传递给yield左边的变量

  • 调用throw()方法,会抛出异常,可以由生成器函数内部捕获,也可以由函数外部捕获

  • 生成器自动执行函数(执行器)co模拟实现

    function co(generator) {
        function handleResult(result) {
            if(result.value.done) return
     
            result.value.then(data => {
                handleResult(generator.next(data))
            }, error => generator.throw(error))
        }
        handleResult(generator.next())
    }
    

async/await

ES2017引入,Generator的语法糖,异步操作自动执行,更加语义化

  • async函数返回一个Promise对象
    • 将await之前的代码放入Promise.resolve()
    • 将await之后的代码放入then
    • 如果执行异常,则放入Promise.reject()
  • await之后是一个Promise对象
    • 则Promise.resolve()回调的参数值作为await表达式的值
    • 抛出Promise.reject()的异常
  • await之后是一个常量,则将值作为参数,返回一个Promise.resolve()

你可能感兴趣的:(学习笔记(二)—— 异步编程)