1.JavaScript 深度剖析-2. JS 异步编程

254 阅读5分钟

★ 概述

js是单线程,为什么是单线程,最早javascript设计的语言就是运行再浏览器端的脚本语言,实现页面动态交互,核心就是DOM操作,所以必须使用单线程模型,

js执行环境中,负责执行代码的线程只有一个

主要内容如下

同步与异步

时间循环与消息队列

异步编程几种方式

Promise异步方案、宏任务、微任务队列

Generator异步方案、es2017async await语法糖


★同步

在调用栈中压入匿名调用,把全部代码放到匿名函数中执行,只有调用的时候才会压入调用栈,声明的时候不会入栈

★异步

不会等待这个任务结束才开始下一个任务

消息队列和事件循环实现的,

call stask 调用栈是一个真正执行的工作表

queue消息队列就是一个代办的工作表

js引擎先去执行调用栈中的任务,然后通过事件循环evenloop,去消息队列中再取一个任务出来,放到调用栈中,依次去执行,以此类推,随时网消息队列queue中放入任务,会排队等待eventloop时间循环,

web apis 就是像settimeout这种,计时器,倒计时,就在这里执行,倒计时结束了,就把任务放到queue任务队列里

虽然js是单线程的,但是浏览器不是单线程的,像settimeout哪些就是用的浏览器的其他线程去执行的,我们说的单线程也只是执行自己js代码的线程是单线程的,

image.png


★ 回调函数


★ promise

概述

承诺,一开始是待定的状态是pending,后面变成成功fulfilled或者失败rejected

链式调用

不要嵌套使用,要链式调用

Promise对象的then会返回一个全新的Promise对象,所以可以使用链式调用的方式进行调用

异常处理

catch是then的别名 相当于then(undefined, onRejected),用catch更适用链式调用

// 使用第二个参数失败回调函数
 ajax('/api/users11.json')
   .then(function onFulfilled (value) {
     console.log('onFulfilled', value)
   }, function onRejected (error) {
     console.log('onRejected', error)
   })
   
 // 使用catch
 ajax('/api/users11.json')
  .then(function onFulfilled (value) {
    console.log('onFulfilled', value)
  })
  .catch(function onRejected (error) {
    console.log('onRejected', error)
  })
   
   
// then(onRejected) 实际上就相当于 then(undefined, onRejected)

ajax('/api/users11.json')
  .then(function onFulfilled (value) {
    console.log('onFulfilled', value)
  })
  .then(undefined, function onRejected (error) {
    console.log('onRejected', error)
  })

全局异常捕获

不建议全局捕获异常,有什么问题就在当前逻辑下面捕获异常处理

window.addEventListener('unhandledrejection', event => {
 const { reason, promise } = event

 console.log(reason, promise)
 // reason => Promise 失败原因,一般是一个错误对象
 // promise => 出现异常的 Promise 对象

 event.preventDefault()
}, false)

// Node.js 中使用以下方式
process.on('unhandledRejection', (reason, promise) => {
  console.log(reason, promise)
  // reason => Promise 失败原因,一般是一个错误对象
  // promise => 出现异常的 Promise 对象
})

静态方法

Promise.resolve('foo')
 .then(function (value) {
   console.log(value)
 })

Promise.reject('anything')
 .catch(function (error) {
   console.log(error)
 })
 
// thenable接口了解
// 如果传入的是带有一个跟 Promise 一样的 then 方法的对象,
// Promise.resolve 会将这个对象作为 Promise 执行

Promise.resolve({
 then: function (onFulfilled, onRejected) {
   onFulfilled('foo')
 }
})
.then(function (value) {
 console.log(value)
})

并行执行

  1. all 等待所有任务结束才算结束
  2. race 等待第一个结束的任务,结果以第一个promise返回结果为准,就是最快的一个promise的返回

promise执行顺序

promise是微任务,直接在当前任务结束过后,立即去执行,不会再回调队列的末尾,而是插队,不用重新排队,提高了整体的相应能力,

settimeout是宏任务的形式进入到回调队列的末尾,大部分都是宏任务,

Promise 、MutationObserver、Process.nextTick 是微任务


★ Generator es2015

Generator

调用、执行、异常

调用:执行带*号的函数,得到的是生成器对象,只有手动调用next()方法,这个函数的函数体才会开始执行

执行:调用next传入的参数,会作为yeild执行的返回值

异常:generator.throw() 带*的函数里面执行异常使用try catch捕获

done 属性为true,生成器结束

// 生成器函数回顾

function * foo () {
  console.log('start')

  try {
    const res = yield 'foo'
    console.log(res)
  } catch (e) {
    console.log(e)
  }
}

const generator = foo()

const result = generator.next()
console.log(result)


// generator.next('bar')

generator.throw(new Error('Generator error'))


// 输出结果
start
{ value: 'foo', done: false }
Error: Generator error

生成器函数执行器co


★ async await

  • 带*函数改成 async yield改成await
  • async 函数返回一个Promise对象
  • await 必须再async函数内部使用

★ Promise 类核心逻辑实现

  1. Promise 就是一个类 在执行这个类的时候 需要传递一个执行器进去 执行器会立即执行
  2. Promise 中有三种状态 分别为 成功 fulfilled 失败 rejected 等待 pending
    pending -> fulfilled
    pending -> rejected
    一旦状态确定就不可更改
  3. resolve和reject函数是用来更改状态的
    resolve: fulfilled
    reject: rejected
  4. then方法内部做的事情就判断状态 如果状态是成功 调用成功的回调函数 
     如果状态是失败 调用失败回调函数 then方法是被定义在原型对象中的
  5. then成功回调有一个参数 表示成功之后的值 then失败回调有一个参数 表示失败后的原因
  6. 同一个promise对象下面的then方法是可以被调用多次的
  7. then方法是可以被链式调用的, 后面then方法的回调函数拿到值的是上一个then方法的回调函数的返回值

new promise ()

()里面传递一个函数,这个函数就是执行器

class promise

class promise 里面通过constructor把执行器接收到,然后调用执行器,就是constructor的参数,执行,立即执行

异步处理

异步处理,添加成功失败的回调函数

多个then方法

多个then方法,就把回调函数初始设置成 数组[ ],使用的时候用push或者shift

链式调用 then的值取决于上一个回调函数的返回值

  • 1.返回一个Promise对象。
  • 2.then的返回值传递给下一个回调函数