总结的内容:
JS异步与单线程的关系(什么是单线程)event-loop事件轮询(JS异步由event-loop实现,异步是怎么执行的)jQuery中异步的解决方案(jQuery中的Deferred)★JS异步与单线程的关系
JS是单线程的。
单线程:只有一个线程、同一时间只能做一件事。两端JS代码不能同时执行 原因:为了避免DOM渲染的冲突。
浏览器需要渲染DOMJS可以修改DOM因此,JS的执行和浏览器渲染DOM的执行共用一个线程。JS执行的时候,浏览器渲染DOM会暂停。(都修改DOM就冲突了)webworker支持多线程,但是不能访问DOM 单线程的解决方案:异步[async/await、promise解决方案](虽然也有问题) 异步解决: 异步解决单线程的问题:没按照书写顺序执行,可读性差callback中不容易模块化★event-loop事件轮询 异步的实现方式event-loop
事件轮询,JS实现异步的具体方案同步代码直接执行异步函数先放入异步队列中待同步函数执行完毕,轮询执行 异步队列 中的函数实例分析一: 等主进程中的函数执行完,将异步队列中的函数放入主进程中执行 实例分析二: 事件轮询执行步骤:
主进程执行到第一个setTimeout,看是个异步,等100ms之后放入异步队列中下来执行到第二个setTimeout,是个异步,直接放入异步队列中主进程直接执行console.log(3);同步代码执行完成后看异步队列中有没有函数这时异步队列中只有第二个setTimeout函数将第二个setTimeout中的函数拿到主进程中执行立即到异步队列中看看还有没有要执行的函数JS引擎会轮询着来看异步队列中有没有100ms之后,第一个setTimeout中的函数放入异步队列JS引擎会立刻将这个函数放入主进程中执行事件轮询event-loop像一个哨兵一样,监视异步队列,只要有函数进入异步队列,立刻将函数拿到主进程中执行,立即又回去监视着
实例分析三 ajax加载完成时放入异步队列 所以实例分析三的结果可能是dcba 也可能是dcab
event-loop总结:
同步代码,直接执行异步函数先放入异步队列中【异步定时0ms直接放入异步队列,异步定时100ms是100ms之后放入异步队列,ajax请求成功后将callback函数放入异步队列】待同步函数执行完毕,轮询执行异步队列的函数★ jQuery中异步的解决方案 jQuery1.5的变化 jQuery1.5之前ajax请求写法:
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对象对修改开放,对扩展封闭,不满足开放封闭原则
jQuery1.5之后ajax请求写法:
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(){ //成功回调 },function(){ //失败回调 }).then(function(){ //成功回调 },function(){ //失败回调 })与promise相似,所以promise是先从jquery开始的。.fail .done .then这种形式是通过jQuery.deferred实现的
对修改封闭,对扩展开放,满足开放封闭原则
jQuery1.5的变化
无法改变JS单线程的本质只能从写法上杜绝callback这种形式它是一种语法糖形式,但是解耦了代码很好的体现了:开放封闭原则(多人开发有质的提高,有错误时好查,极大地减少了有错误时排查错误的能力)jQuery Deferred的使用:
var wait=function(){ var task=function(){ console.log("执行完成"); } setTimeout(task,2000); } wait(); //新增需求,要在执行完成之后进行某些特别复杂的操作,代码可能会很多,而且分好几个步骤。 //可以直接在console.log("执行完成")后面写,但不符合开放封闭原则,所以不能这样写,需要使用jQuery Deferred使用jQuery Deferred:
function waitHandle() { var dtd = $.Deferred(); //创建一个deferred对象 var wait = function (dtd) { //要求传入一个deferred对象 var task = function (){ console.log("执行完成"); dtd.resolve(); //表示异步任务已经完成 //dtd.reject(); //表示异步任务失败或是出错 } setTimeout(task,2000); return dtd //要求返回deferred对象 } //注意,这里一定要有返回值 return wait(dtd); } var w=waitHandle(); //deferred对象 w.then(function(){ console.log("success1"); },function(){ console.log("error1"); }).then(function(){ console.log("success2"); },function(){ console.log("error2"); })多个.then,对扩展开放,对修改封闭,满足开放封闭原则,若都写在console.log(“执行完成”)后面,则不满足开放封闭原则。
Deferred总结:
dtd的API可分为两类:
dtd.resolve dtd.reject (系统主动执行)dtd.then(监听) dtd.done dtd.fail (被动调用) dtd.then包含两个参数,若成功,执行第一个参数,即dtd.resolve。若失败,执行第二个参数,即dtd.reject。 注意:这两类API不能混合使用jQuery1.5之后,wait函数返回的是dtd.promise()。
function waitHandle() { var dtd = $.Deferred(); //创建一个deferred对象 var wait = function (dtd) { //要求传入一个deferred对象 var task = function (){ console.log("执行完成"); dtd.resolve(); //表示异步任务已经完成 //dtd.reject(); //表示异步任务失败或是出错 } setTimeout(task,2000); return dtd.promise() //要求返回deferred对象 }; //注意,这里一定要有返回值 return wait(dtd); } var w=waitHandle(); //经过上面的改动,w接收的是一个promise对象 w.then(function(){ console.log("success1"); },function(){ console.log("error1"); }).then(function(){ console.log("success2"); },function(){ console.log("error2"); }); w.reject(); //执行这句话直接会报错 因为返回promise对象,则w只有.then .done .fail这种被动监听的方法,把dtd的reject resolve给过滤掉了,外面不能再调用,只有我封装的时候才可以调用。(promise和deferred的对比)deferred可以主动调用reject和resolve方法,所以有可能被别人篡改代码,而promise只能调用.then .done .fail这种被动监听的方法,别人不能修改,比较安全,也满足开放封闭原则。
(promise和deferred的区别)Deferred实例既可以执行.resolve .reject这种主动执行的函数,又可以执行.then .fail .down这种被动监听结果的函数,但这两类放在一起会发生冲突,又封装出了Promise对象,Promise只能去被动监听.done .then .fail这种结果,不能主动干预失败成功的操作,完美解决了问题