前端高级面试问到关于异步的问题
导言:说到异步,你可能会想到event-loop Promise Async/Await等,下面我们就来聊聊这几个问题:
- 什么是单线程,和异步有什么关系
- 什么是event-loop
- 目前JS解决异步有哪些方案
- 如果使用jquery如何解决异步
- Promise标准
- async/await的使用
什么是单线程,和异步有什么关系
- 单线程 —— 只有一个单线程,只能做一件事情(代码演示)
// 循环运行期间,JS 执行和 DOM 渲染暂时卡顿
var i, sum = 0;
for (i = 0; i < 100000000; 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就冲突了)
- webworder支持多线程,但不能访问DOM
- 解决方案 —— 异步(代码演示)
console.log(100)
setTimeout(function () {
console.log(200) //反正 1000ms 之后执行
},1000) //先不管他,先让其他JS代码运行
console.log(300)
console.log(400)
console.log(100)
$.ajax({
url:'xxx',
success: function (result) { //ajax 加载完才执行
console.log(result) //先不管他,先让其他JS代码运行
}
})
console.log(300)
console.log(400)
异步存在的问题
问题一:没有按照格式执行,可读性差
问题二:callback 中不容易模块化
问题解答:
1.单线程就是只能做一件事,两段js不能同时执行
2.原因就是为了避免dom渲染冲突
3.异步是一种无奈的解决方案,虽然有很多问题
什么是event-loop
知识串联:
1.单线程-同时只能做一件事
2.原因避免DOM渲染冲突
3.解决方案:异步
4.实现方式:event-loop
文字解释
- 事件轮询,JS实现异步的具体方案
- 同步代码,同步执行
- 异步函数先放在异步队列中
- 待同步函数 执行完毕 ,轮询执行 异步队列函数
实例分析
第一种:
setTimeout(function () {
console.log(100)
})
console.log(200)
第二种:
setTimeout(function () {
console.log(1)
},100)
setTimeout(function () {
console.log(2)
})
console.log(3)
第三种:
$.ajax({
url: 'xxxx',
success : function (result) {
console.log('a')
}
})
setTimeout(function () {
console.log('b')
},100)
setTimeout(function () {
console.log('c')
})
console.log('d')
问题解答
1.事件轮询,JS异步的解决方案
2.什么是异步队列,何时放入异步对列
3.轮询的过程
目前JS解决异步有哪些方案
1.jQuery deferred
2.Promise
3.Async/Await
4.Generator
如果使用jquery如何解决异步
-
是否使用过jquery的Deferred
- jquery 1.5的变化
-
jquery 1.5的变化 - 1.5 之前
var ajax = $.ajax({ url:'XXX', success: function () { console.log('success1') console.log('success2') console.log('success3') }, error: function () { console.log('error') } }) console.log(ajax) //返回一个XHR对象
-
jquery 1.5的变化 - 1.5 之后
var ajax = $.ajax('xxxx'); ajax.done(function () { console.log('success1') }).fail(function () { console.log('error') }).done(function () { console.log('success2') }) console.log(ajax) //返回一个Deferred对象
//很像Promise的写法 var ajax = $.ajax('xxx') ajax.then(function () { console.log('success 1') },function () { console.log('error 1') }).then(function () { console.log('success 2') },function () { console.log('error 2') })
问题解答:
1.无法改变JS异步和单线程的本质 2.只能从写法上杜绝callback这种形式 3.它是一种语法糖形式,但是解耦了代码 4.很好体现:开放封闭原则
- 使用jQuery Deferred
// 给出一段很简单的异步操作,使用setTimeout函数 var wait = function () { var tast = function () { console.log('执行完成') } setTimeout(tast, 2000) } wait() //新增需求:要在执行完成之后进行某些特别复杂的操作,代码可能很多,而且分好几个步骤
function waitHandle () { var dtd = $.Deferred() //创建一个Deferred 对象 var wait = function (dtd) { //要求传入一个Deferred 对象 var tast = function () { console.log('执行完成') dtd.resolve() //表示异步任务已经完成 //dtd.reject() //表示异步任务失败或出错 } setTimeout(tast, 2000) return dtd } //注意,这里一定要有返回值 return wait(dtd) } var w = waitHandle() w.then(function () { console.log('ok 1') },function () { console.log('error 1') }).then(function () { console.log('ok 2') },function () { console.log('error 2') }) //还有 dtd.done dtd.fail
问题解答:
1.总结:dtd的API可分为两类,用意不同 2.第一类:dtd.resolve dtd.reject 3.第二类:dtd.then dtd.done dtd.fail 4.这两类应该分开,否则后果很严重 5.可以在上面的代码最后执行dtd.reject() 试一下后果 6.使用dtd.promise
function waitHandle () { var dtd = $.Deferred() var wait = function (dtd) { var tast = function () { console.log('执行完成') dtd.reject() } setTimeout(tast, 2000) return dtd.Promise() //注意,这里返回的是Promise,而不是直接返回Deferred对象 } return wait(dtd) }
var w = waitHandle() //经过上面的改动,w接收一个Promise对象 $.when(w) .then(function () { console.log('task 1') }) .then(function () { console.log('task 2') }) //w.reject() //执行这个会报错
3.初步引入Promise概念
问题解答:
1.可以jquery 1.5 对ajax的改变举例 2.说明如何简单封装,使用Deferred 3.说明Promise和Deferred的区别
Promise 的标准
Promise 的基本使用和原理
- 基本语法的回顾
function loadImg (src) {
const promise = new Promise(function (resolve, reject) {
var img = document.createElement('img')
img.onload = function () {
resovle(img)
}
img.onerror = function () {
reject()
}
img.src = src
})
return promise
}
var src = 'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1583670709077&di=30593978e23e2d1c6830cbdd7d2bdd18&imgtype=0&src=http%3A%2F%2Fwww.piaodown.com%2Fupload%2F20187%2F2018072626027685.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.hieght)
}).catch(function (ex) {
//最后统一用 catch
console.log(ex)
})
- 多个串联
var src1 = 'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1583670709077&di=30593978e23e2d1c6830cbdd7d2bdd18&imgtype=0&src=http%3A%2F%2Fwww.piaodown.com%2Fupload%2F20187%2F2018072626027685.png'
var result1 = loadImg(src1)
var src2 = 'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1583739408720&di=71d5005f34a42c7ee37e05e4f48f4c83&imgtype=0&src=http%3A%2F%2Fa3.att.hudong.com%2F68%2F61%2F300000839764127060614318218_950.jpg'
var result2 = loadImg(src2)
result1.then(function (img) {
console.log('第一张图片', img.width)
return result2
}).then(function (img) {
console.log('第二张图片', img.height)
}).catch(function (ex) {
console.log(ex)
})
- Promise.all和Promise.race
//Promise.all 接收一个Promise 对象的数组
//待全部完成之后,统一执行 success
Promise.all([result1, result2]).then(datas => {
//接收data是一个数组,依次包含多个Promise 返回的内容
console.log(datas[0])
console.log(datas[1])
})
//Promise.race 接收一个包含多个Promise对象数组
//只要有一个完成,就执行success
Promise.race([result1, result2]).then(data => {
//data 即最先执行完成的Promise的值
console.log(data)
})
-
Promise标准
1.关于"标准"的闲谈
- 任何技术推广需要使用一套标准来支撑 - 如html,js,css,http等,无规矩不成方圆 - 任何不符合标准的东西,终将被用户抛弃 - 不要挑战标准,不要自造标准
2.状态的变化
- 三种状态:pending fulfilled rejected - 初始状态为pending - pending 变为 fulfilled ,或者pending 变为 reject - 状态变化不可逆
3.then
- Promise 实例必须实现then这个方法 - then() 必须可以接受两个函数参数 - then() 返回的必须是一个Promise 实例
代码实例:
var result = loadImg(src) result.then(function (img) { console.log(img.width) },function () { console.log('failed') }).then(function (img) { console.log(img.height) })
result1.then(function (img) { console.log('第一张图片加载完') return result2 }).then(function (img) { console.log('第二张图片加载完') })
问题解答:
- 基本语法
- 如何异常捕获
- 多个串联 - 链式执行的好处
- Promise.race 和 Promise.all
- Promise的标准-状态变化,then函数
async/await 的使用
- then只是将callback拆分
var w = waitHandle()
w.then(function () {
console.log('ok 1')
},function () {
console.log('err 1')
}).then(function () {
console.log('ok 2')
},function () {
console.log('err 1')
})
- async/await是最直接的同步写法
const load = async function () {
const result1 = await loadImg(src1)
console.log(result1)
const resutl2 = await loadImg(src2)
console.log(result2)
}
load()
-
语法
1.使用await,函数必须用async标识
2.await后面跟的是一个Promise实例
3.需要babel-polyfill
function loadImg () {
var promise = new Promise(function (resolve, reject) {
var img = document.createElement('img')
img.onload = function () {
resovle(img)
}
img.onerrror = function () {
reject()
}
img.src = img
})
return promise
}
var src1 = 'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1583670709077&di=30593978e23e2d1c6830cbdd7d2bdd18&imgtype=0&src=http%3A%2F%2Fwww.piaodown.com%2Fupload%2F20187%2F2018072626027685.png'
var src2 = 'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1583739408720&di=71d5005f34a42c7ee37e05e4f48f4c83&imgtype=0&src=http%3A%2F%2Fa3.att.hudong.com%2F68%2F61%2F300000839764127060614318218_950.jpg'
const load = async function () {
const result1 = await loadImg(src1)
consoel.log(result1)
const result2 = await loadImg(src2)
console.log(result2)
}
load()
问题解答:
a.基础语法
b.使用Promise,并没有跟Promise冲突
c.完全是同步写法,再也没有回调函数
d.但是改变不了JS单线程,异步的本质
总结:以上是关于当前异步的解决方案,如果觉得文章不错,希望你能给小🐟的文章轻轻的点个赞,希望能够更多的面试者带来帮助,谢谢你!