JS之同步与异步

122 阅读3分钟

JS单线程和异步

  • JS是单线程语言,只能做一件事
  • 浏览器和nodejs已支持JS启动进程,如web worker
  • JS和DOM渲染共用同一线程,因为JS可以修改DOM结构
  • 遇到等待(网络请求,定时任务)不能卡住,所以需要异步
  • 异步是以callback函数形式
  • promise解决了callback hell

JS同步与异步

基于JS是单线程语言,异步不会阻塞代码执行,同步会阻塞代码执行

JS异步应用场景

  • 网络请求:ajax图片加载
  • 定时任务:setTimeout

手写一个promise加载图片

    function loadImg(src) {
        const p = new Promise(
            (resolve, reject) => {
                const img = document.createElement('img')
                img.onload = () => {
                    resolve(img)
                }
                img.onerror = () => {
                    const error = new Error('图片加载失败')
                    reject(error)
                }
                img.src = src
            }
        )
        return p
    }
    const url = "https://juejin.cn/user/2"
    const url2 = "https://juejin.cn/1"
    loadImg(url).then(img => {
        console.log(img.width);
        return img //普通对象
    }).then(img => {
        console.log(img.height);
        return loadImg(url2) //promise 实例
    }).then(img2 => {
        console.log(img2.width);
        return loadImg(url2)
    }).catch(ex => {
        console.log(ex);
    })

JS执行机制

  • 从前往后,一行一行执行
  • 如果某一行执行错误,则停止下面代码的执行
  • 先把同步代码执行完,再执行异步

event loop(事件循环/事件轮询)

异步是基于callback来实现,event loop是异步回调的实现原理

过程:

  1. 同步代码,一行一行放在call stack执行
  2. 遇到异步会先"记录"下,等待时机(定时、网络请求等)
  3. 时机一到就放在callback queue
  4. 如遇call stack为空(即同步代码执行完),event loop开始工作
  5. 轮询查找callback queue,如有则移动到call stack执行
  6. 然后继续轮询查找(永动机一样)

Promise

三种状态:

  • Pending(进行中,初始状态,既不是成功,也不是失败状态)
  • Resolved(已完成,又称 Fulfilled)
  • Rejected(已失败)

这三种状态的变化途径只有2种:

  • 异步操作从 未完成 pending => 已完成 resolved
  • 异步操作从 未完成 pending => 失败 rejected
  • 状态一旦改变,就无法再次改变状态

then和catch改变状态 then正常返回resolved,里面有报错则返回 rejected catch正常返回resolved,里面有报错则返回 rejected

async/await

为了解决异步回调callback hell,Promise then catch链式调用,但也是基于回调函数,async/await是同步语法,彻底消灭回调函数

async/await和Promise的关系

async/await是消灭异步回调的终极武器,但和Promise并不互斥,反而相辅相成

总结: 异步本质

  • async/await是消灭异步回调的终极武器
  • JS是单线程语言,还是得有异步,还是得基于event loop
  • async/await本质只是一个语法糖

for...of常用于异步遍历

宏任务macroTask和微任务microTask

  • 宏任务macroTask:setTimeout,setInterval,Ajax,DOM事件
  • 微任务microTask:Promise,async/await
  • 微任务执行时机比宏任务要早,微任务是在DOM渲染前触发,宏任务是在DOM渲染后触发

event loop和DOM渲染

  1. 每次call stack清空空(即同步代码执行完)
  2. 都是DOM重新渲染的机会,DOM结构如有改变则重新渲染
  3. 然后再次去触发event loop