前端面试(二)异步

302 阅读2分钟

单线程和异步有何关系

  • 单线程:只有一个线程,只能做一件事
  • 原因:避免DOM渲染冲突

(1)浏览器需要渲染DOM

(2)JS可以修改DOM结构

(3)JS执行的时候,浏览器DOM渲染会暂停

(4)两段JS也不能同时执行(都修改DOM就冲突了)

(5)HTML5中webworker支持多线程,但不能访问DOM(应用不广泛)

  • 解决方案:异步(先让其他代码先执行,异步执行的后面执行)

console.log(100)
$.ajax({
  url: 'xxx',
  success: function (result) { // ajax加载完才执行
    console.log(result) // 先不管它,先让其他JS代码运行
  }
})
console.log(300)
console.log(400)

问题:

(1)没有按照书写方式执行,可读性差

(2)callback中不容易模块化(异步之后需要执行的函数)

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

  • 什么是event-loop(JS实现异步的具体解决方案)

(1)同步代码,直接执行

(2)异步函数先放在异步队列中

(3)待同步函数执行完毕,轮询执行异步队列的函数(像有个哨兵一直进行监视,异步队列中一有函数就开始执行)

  • 实例分析

setTimeout(function () {
  console.log(100)
})
console.log(200)

// 主进程
console.log(200)

// 异步队列
function () {
  console.log(100)
}

setTimeout(function () {
  console.log(1)
}, 100)
setTimeout(function () {
  console.log(2)
})
console.log(3)

// 主进程
console.log(3)

// 异步队列
// 立即加入
function () {
  console.log(2)
}

// 100ms 之后被放入
function () {
  console.log(1)
}

  • 实战

目前JS解决异步的方案有哪些


如果只用jquery如何解决异步(jQuery Deferred)

  • 无法改变JS异步和单线程的本质
  • 只能从写法上杜绝callback这种写法
  • 它是一种语法糖形式,但是解耦了代码
  • 很好的体现:开放封闭原则(对扩展开放对修改封闭)

// jQuery1.5之前
var ajax = $.ajax({
  url: 'xxx',
  success: function() {
    // 没有地方进行扩展只能在这里进行修改
  },
  error: function() {

  }
})

console.log(ajax) // 返回一个XHR对象

// jQuery 1.5之后 需要新加代码时可以直接只加个done或者then进行扩展
var ajax = $.ajax('xxx')
ajax.done(function() {

}).fail(function() {

}).done(function() {})

ajax.then(function () {
  console.log('success 1')
}, function () {
  console.log('error 1')
}).then(function () {
  console.log('success 2')
}, function () {
  console.log('error 2')
})

jQuery Deferred 和 Promise的区别?

(1)jQuery Deferred 有resolve() 、reject()主动触发的函数也有then()、done()、error()被动监听的函数

(2)Promise 只能被动监听 不能主动修改

// 已经封装好的(A 员工)
function waitHandle() {
    // 定义
    var dtd = $.Deferred()
    var wait = function (dtd) {
        var task = function () {
            console.log('执行完成')
            // 成功
            dtd.resolve()
            // 失败
            // dtd.reject()
        }
        setTimeout(task, 1000)
        // wait 返回
        return dtd.promise()
    }
    // 最终返回
    return wait(dtd)
}

// 使用(B 员工)
var w = waitHandle()  // promise 对象
$.when(w).then(function () {
    console.log('ok 1')
}, function () {
    console.log('err 1')
})

Promise的标准

  • 基本语法回顾

function loadImg(src) {
  const promise = new Promise(function (resole, reject) {
    var img = document.createElement('img')
    img.onload = function () {
      resolve(img) // 解决
    }
    img.onerror = function () {
      reject() // 拒绝
    }
    img.src = src
  })
  return promise
}
var src = '图片地址'
var result = loadImg(src)
result.then(function (img) {
  console.log(img.width)
},function () {
  console.log(failed)
})
result.then(function (img) {
  console.log(img.height)
})

  • 异常捕获(throw error和reject都可以用)

规定:then只接受一个参数,最后统一用catch捕获异常

  • 多个串联
  • Promise.all 和 Promise.race
  • Promise 标准

(1)三种状态:pending、fulfilled、rejected;初始状态是:pending;pending变为fulfilled,或者pending变成rejected(状态变化不可逆)

(2)Promise 实例必须实现then 这个方法;then()必须可以接受两个函数作为参数;then返回必须是一个Promise实例(没有返回时默认被调用的实例); then只是将callback拆分了

async/await的使用(ES7): 最直接的同步写法

  • 使用了Promise,并没喝Promise冲突
  • 完全是同步的写法,再也没有回调函数
  • 改变不了JS单线程异步的本质