美文网首页
前端面试之异步

前端面试之异步

作者: 小雪狸 | 来源:发表于2020-09-15 15:58 被阅读0次

++本文系慕课网学习笔记++

什么是单线程,和异步有什么关系

解答思路:

  1. 单线程就是只能做一件事,两端 JS 不能同时执行
  2. 原因就是为了避免 DOM 渲染的冲突
  3. 异步是一种“无奈”的解决方案,虽然有很多问题

单线程 - 只有一个线程,只能做一件事

// 循环运行期间, JS 执行和 DOM 渲染暂时卡顿
var i, sum = 0
for (i=0; i<1000000000; i++) {
    sum += i
}
console.log(sum);

// alert 不处理,JS 执行和 DOM 渲染暂时卡顿
console.log(1)
alert('Hello')
console.log(2)
  • 原因 =- 避免 DOM 渲染的冲突
    • 浏览器需要渲染 DOM
    • JS 可修改 DOM 结构
    • JS 执行的时候,浏览器 DOM 渲染会暂停
    • 两段 JS 也不能同时执行(都修改一个 DOM 就冲突了)
    • webworker 支持多线程,但是不能访问 DOM
  • 解决方案 - 异步
console.log(100)
setTimeout(function() {
    console.log(200) // 先不管它,先让其他 JS 代码执行,1000ms后执行
}, 1000)
console.log(300)
console.log(400)
console.log(100)
$.ajax({
    url: 'xxx',
    success: function(result) {  // ajax 加载完才执行,先让其他 JS 代码执行
        console.log(result)
    }
})
console.log(300)
console.log(400)
  • 问题
    • 没有按照书写方式执行,可读性差
    • callback 中不容易模块化

什么是 event-loop 事件轮询

解答思路:

  1. 事件轮询,JS 异步的解决方案
  2. 什么是异步队列,何时被放入异步队列
  3. 轮询的过程
  • 文字解释
    • 事件轮询, JS 实现异步的具体解决方案
    • 同步代码,直接执行
    • 异步函数先放在异步队列中
    • 待同步函数执行完毕,轮询执行 异步队列的函数
  • 实例分析
// setTimeout(function() {
//     console.log(100)
// }, 1000)
// 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)
}

是否用过 jquery 的 Deferred

解答思路:

  1. 可以 jQuery 1.5 对 ajax 的改变举例
  2. 说明如何简单的封装,使用 Deferred
  3. 说明 promise 和 Deffered 的区别(reject 和 resolve)
  • jQuery 1.5 的变化
    • 无法改变 JS 异步和单线程的本质
    • 只能从写法上杜绝 callback 的形式
    • 它是一种语法糖形式,但是解耦的代码
    • 很好的体现了开放封闭原则(扩展开放, 修改封闭)
<!--  jQuery 1.5 之前 -->
var ajax = $.ajax({
    url: 'data.json',
    success: function() {
        console.log('success1')
        console.log('success2')
        console.log('success3')
    },
    error: function() {
        console.log('error')
    }
})
console.log(ajax) // 返回一个 XHR 对象


<!--  jQuery 1.5 之后 -->
var ajax = $.ajax('data.json')
ajax.done(function() {
    console.log('success1')
})
.fail(function() {
    console.log('error')
})
.done(function() {
    console.log('success2')
})
console.log(ajax) // 返回一个 deferred 对象


// 很像 Promise 的写法
var ajax = $.ajax('data.json')
ajax.then(function() {
    console.log('success1')
}, function() {
    console.log('error1')
})
.then(function() {
    console.log('success2')
}, function() {
    console.log('error2')
})
  • 使用 jQuery Deferred
    • 总结, dtd 的 API 可以分成两类,用意不同
    • 第一类: dtd.resolve dtd.reject(主动触发)
    • 第二类:dtd.then dtd.done dtd.fail(被动监听)
    • 这两类应该跟开使用,否则后果很严重
function waitHandle() {
    var dtd = $.Deferred() // 创建一个 Deferred 对象
    var wait = function(dtd) { // 要求传入一个 Deferred 对象
        var task = function() {
            console.log('执行完成')
            dtd.resolve()  // 表示异步任务已经完成
            // dtd.reject() // 表示异步任务失败或出错
        }
        setTimeout(task, 200)
        return dtd // 要求返回 Deferred 对象
    }
    // 这里一定要有返回值
    return wait(dtd)
}
var w = waitHandle()
w.then(function() {
    console.log('ok1')
}, function() {
    console.log('err1')
})
.then(function() {
    console.log('ok2')
}, function() {
    console.log('err2')
})
  • 引入 Promise 概念
function waitHandle() {
    var dtd = $.Deferred() 
    var wait = function(dtd) { 
        var task = function() {
            console.log('执行完成')
            dtd.promise() // 注意这里返回的是 Promise,而不是直接返回 Deferred 对象
        }
        setTimeout(task, 200)
        return dtd 
    }
    return wait(dtd)
}

Promise 的基本使用和原理

解答思路:

  1. 基本语法
  2. 如何捕获异常
  3. 多个串联-链式执行的好处
  4. Promise.all 和 Promise.race
  5. Promise 标准, 状态变化, then函数
  • 基本语法回顾
function loadImg(src) {
    const promise =  new Promise(function(resolve, reject) {
        var img = document.createElement('img')
        img.onload = function() {
            resolve(img)
        }
        img.onerror = function() {
            reject()
        }
        img.src = src
    })
    return promise
}

var src = "http://www.xxxx.xxx.png"
var result = loadImg(src)

result.then(function(img) {
    console.log(img.width)
}, function() {
    console.log('failed')
}).then(function(img) {
    console.log(img.height)
})
  • 异常捕获
// 规定 then 函数只接受一个参数,最后统一用 catch 捕获异常
result.then(function(img) {
    console.log(img.width)
}).then(function(img) {
    console.log(img.height)
}).catch(function(ex) {
    console.log(ex)
)}
  • 多个串联
var src1 = 'xxx1.jpg'
var src1 = 'xxx2.jpg'
var result1 = loadImg(src1)
var result2 = loadImg(src2)
// 链式操作
result1.then(function(img) {
    console.log('第一个图片加载完成')
    return result2
}).then(function(img) {
    console.log('第二个图片加载完成')
}).catch(function(ex) {
    console.log(ex)
)}
  • Promise.all 和 Promise.race
    • race 先执行, all后执行
// Promise.all 接收一个 promise 对象的数组
// 待全部完成之后,统一执行 success
Promise.all([result1, result2]).then(datas => {
    console.log(datas[0])
    console.log(datas[1])
})

// Promise.all 接收一个包含多个 promise 对象的数组
// 只要有一个完成,就执行 success
Promise.race([result1, result2]).then(data => {
    // data 即是最先执行完成的 promise 的返回值
    console.log(data)
})
  • promise 标准
    • 关于“标准”的闲谈
      • 任何技术推广使用都需要一套标准来支撑
      • 如 html js css http 等,无规矩不成方圆
      • 任何不符合标准的东西,终将被用户抛弃
      • 不要挑战标准,不要自造标准
    • 状态变化
      • 三种状态:pending fulfilled rejected
      • 初始状态是 pending
      • pending 变为 fulfilled ,或者 pending 变为 rejected
      • 状态变化不可逆
    • then
      • Promise 实例必须实现 then 这个方法
      • then() 必须可以接收两个函数作为参数
      • then() 返回的必须是一个 Promise 实例

介绍一下 async/await(Promise 的区别、联系)

解答思路:

  1. 基本语法
  2. 使用了Promise, 并没有和 Promise 冲突
  3. 完全是同步的写法, 再也没有回调函数
  4. 但是改变不了 JS 单线程、异步的本质
  • then 知识将 callback 拆分了
  • async/await 是最直接的同步写法
    • 使用 await,函数必须用 async 标识
    • await 后面跟的是一个 Promise 实例
    • 需要 babel-polyfill
const load = async function() {
    const result1 = await loadImg(src1)
    console.log(result1)
    const result2 = await loadImg(src2)
    console.log(result2)
}
load()

总结一下当前 JS 解决异步的方案

解答思路:

  1. jQuery Deferred
  2. Promise
  3. Async/await
  4. Generator(原理比较复杂,不是异步的直接替代方式,有更好更简洁的解决方案 async/await, koa也早已“弃暗投明”)

相关文章

网友评论

      本文标题:前端面试之异步

      本文链接:https://www.haomeiwen.com/subject/ryefyktx.html