三、《揭秘一线互联网企业 前端JavaScript高级面试》视频教程总结系列三:JS异步 相关
系列目录:https://www.cnblogs.com/lmyxywy/p/10931603.html
问题一:单线程是什么?和异步有什么联系?
单线程是同一时间只做一件事,两段js不能同时执行。
这是因为js可以操作Dom,为了避免两段js同时修改一个Dom元素从而发生冲突,所以js的作者采用了单线程机制。
另外,因为浏览器的UI渲染需要渲染Dom元素,所以为了避免和js起冲突,UI渲染和js也是在同一个线程里的;
其中js修改Dom的时候,UI渲染需要暂停。
两者单线程的实质都是为了避免Dom渲染冲突。
根据单线程的原理,一件事没做完之前程序就不往下走,这样势必就会导致等待,等待时间长了次数多了就会造成阻塞。
异步就是解决单线程阻塞问题的一种“无奈”的解决方案。
异步的原理:通俗来讲,就是遇到需要等待的事件,先略过,继续往下走同步的事件,等到到达处理异步事件的时间时,再回头执行异步事件。
异步导致的问题:
1、不按照书写顺序执行,可读性非常差,不利于理解和调试;
2、callback(回调函数)嵌套层级非常多,耦合度相当高,不容易模块化。
问题二:什么是event-loop?什么是异步队列,何时被放入异步队列?
event-loop是事件轮询,JS实现异步的具体解决方案。
event-loop执行原理:
1、所有同步任务都放在主线程中执行,形成一个执行栈;
2、遇到异步任务,暂不执行,将任务插入到异步队列中,它是主线程外专门存放异步任务的队列;
放入异步队列的时机分三种情况:
①没有规定延迟时间,立刻放入异步队列中;
②有延迟时间,到了延迟时间再放入异步队列中;
③类似Ajax,需要等待返回的数据,何时接收到数据何时放入异步队列。
3、等到主进程中的所有同步任务执行完毕时,监听异步队列,轮询执行异步任务。
(轮询是指:只要主线程空了,异步队列中的任务就会进入执行栈,等待执行栈执行完毕,系统再去异步队列中监听读取任务,让异步任务进入执行栈,循环往复)
问题三:是否使用过Jquery的Deferred?
Jquery Deferred是Promise的前身,改变了异步任务必须使用回调函数来书写的方式。
1、先介绍下deferred的使用实例——Jquery1.5前后Ajax写法的变化
Jquery1.5之前,没采用deferred,异步只能采用callback(回调函数)的形式,代码都堆在一块,无法模块化,不便于扩展(对修改开放、对扩展封闭)。
var ajax = $.ajax({
url:'data.json',
success:function () {
console.log('success 1');
console.log('success 2');
console.log('success 3');
},
error:function () {
console.log('error');
}
});
console.log(ajax); // 返回一个XHR对象
Jquery1.5之后,采用了deferred,deferred对象调用的第一种写法,异步可以采用链式调用,done、fail两个函数成对出现,将异步函数按照同步的逻辑书写与执行,代码实现了模块化。
var ajax = $.ajax('data.json');
ajax.done(function () {
console.log('success 1');
})
.fail(function () {
console.log('error 1');
})
.done(function () {
console.log('success 2');
})
.fail(function () {
console.log('error 2');
})
.done(function () {
console.log('success 3');
})
.fail(function () {
console.log('error 3');
})
console.log(ajax); // 返回一个deferred对象
Jquery1.5后采用deferred,调用的第二种写法,也是链式调用,函数和容错函数都包含在了then里面,已经相当接近ES6中的Promise了。
var ajax = $.ajax('data.json');
ajax.then(function() {
console.log('success 1');
}, function() {
console.log('error 1');
})
.then(function() {
console.log('success 2');
}, function() {
console.log('error 2');
})
.then(function() {
console.log('success 3');
}, function() {
console.log('error 3');
})
Jquery1.5之后Ajax的写法,体现了五大设计原则中的开放封闭原则(对修改封闭、对扩展开放),更加方便开发人员的修改、扩展与协同开发。
它以一种语法糖的形式解耦了代码,是从写法上杜绝了callback这种形式,并不能改变js单线程和异步的本质。
2、介绍Jquery Deferred 的具体用法,封装一个deferred(非常类似于封装Promise)
原始代码及需求
// setTimeout模拟异步
var wait = function() {
var task = function() {
console.log('打印一')
}
setTimeout(task, 2000);
}
wait()
// 需求为,在'打印一'后每个2秒分别打印'打印二','打印三'
使用Jquery的deferred,将函数封装之后
function waitHandle() {
var dtd = $.Deferred(); // 创建一个deferred对象
return wait = function (dtd) { // 传入deferred对象,并作为promise对象返回
var task = function () {
console.log('打印一')
dtd.resolve() // 表示异步任务已完成
}
setTimeout(task, 2000);
return dtd.promise() // 要求返回promise对象,防止外部调用dtd.resolve()或dtd.reject()阻止进程
}
}
调用封装好的deferred方法,这里用的是then,另一种写法还可以将一个then()替换成一对的done(),fail()
var w = waitHandle()
w.then(function() {
console.log('打印二')
})
.then(function() {
console.log('打印三')
})
以上便是deferred的用法。
总体回答汇总:
Jquery Deferred是Promise的前身,改变了异步任务必须使用回调函数来书写的方式。
使用deferred后,可以将异步函数按照同步的逻辑书写与执行,代码实现了简单的模块化。
Jquery1.5之后Ajax的写法使用了deferred,可以链式调用,体现了五大设计原则中的开放封闭原则(对修改封闭、对扩展开放),更加方便开发人员的修改、扩展与协同开发。
它以一种语法糖的形式解耦了代码,虽然从写法上杜绝了callback这种形式,但并没有改变js单线程和异步的本质。
deferred有两类API:
第一类是dtd.resolve()、dtd.reject(),这类是主动触发的方法;
第二类是dtd.then()或dtd.done()、dtd.fail(),这一类是被动接受的方法;
两类方法不可以在一起混用,否则会造成程序混乱。
所以为了避免这一问题,在封装deferred的时候,最终返回的应该是dtd.promise(),
这样就过滤掉了dtd.resolve()和dtd.reject(),使外部无法调用deferred对象的resolve()、reject()。
问题四:Promise的基本使用和原理。
1、基本语法
①封装:
——new 一个Promise对象,传入一个函数,函数有两个参数,分别是resolve()和reject()方法;
——在Promise对象内部书写具体逻辑,成功则resolve(), 失败则reject ();
——最后返回new出来的这个Promise对象。
②调用:使用.then()链式调用,then接收两个函数,一个是成功函数,一个是失败函数。
(以下我就偷个懒不放代码,直接放截图辣)
2、异常捕获
每个then都接收两个函数比较麻烦,使用异常捕获就方便许多,
异常捕获规定then只接受一个参数,最后统一用catch捕获异常。
catch可以捕获两种错误,一种是逻辑之外的错误(语法报错),一种是逻辑内的错误(reject抛出的错误)。
3、多个串联
在前一个then中返回一个Promise对象,下一个then接收参数的就是上一个Promise对象resolve出来的结果。
下班啦,未完待续。。。
4、Promise.all() 、Promise.race()
5、Promise的标准
问题五:介绍一下async/await(和Promise的区别和联系)