ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
## 异步 1. 异步的基础 2. 高级内容 3. 如event-loop Promise Async/Await等 ### 问题 1. 什么是单线程,和异步有什么关系 2. 什么是event-loop(事件轮询) 3. 是否用过jQuery的Deferred 4. Promise的基本使用和原理 5. 介绍一下async/await(和Promise的区别,联系)(es7) 6. 总结一下当前js解决异步的方案 ### 什么是单线程和异步的关系 * 单线程:只有一个线程,同一时间只能做一件事情 * 原因:避免DOM渲染的冲突 * 解决方案:异步(高效选择,但有一些代码层面的问题) 单线程:只能关注一件事件 **循环运行期间,js执行和dom渲染暂时卡顿** **alert不处理,js执行和dom渲染暂时卡顿** ~~~ //循环运行期间,js执行和dom渲染暂时卡顿 var i,sum = 0; for(i=0;i<1000000000;i++){ sum++; } console.log(sum); //alert不处理,js执行和dom渲染暂时卡顿 console.log(1); alert('hello');//不点击确认,2不会打印出来 console.log(2) ~~~ ### 原因--避免DOM渲染冲突 1. 浏览器需要渲染DOM 2. JS可以修改DOM 3. JS执行的时候,浏览器DOM渲染会停止 4. 两段JS不能同时执行(都修改dom就冲突了) js执行是单线程,js的执行和浏览器的渲染公用一个线程,js本身也是单线程的 5. webworker支持多线程,但是不能访问DOM ### 解决方案-异步(单线程影响执行效率) ~~~ console.log(100); setTimeout(function(){ console.log(200); },1000);//反正1000ms之后执行,先不管它,先让其他js代码运行 console.log(300); console.log(400); ~~~ 输出顺序和代码写的顺序不一样 ~~~ console.log(100); $.ajax({ url:'xxx', url:'./data.json',//可直接请求文件 success:function(result){ console.log(result) } }) //ajax加载完才执行,先不管它,先让其他js代码运行 console.log(300); console.log(400); ~~~ 1. 问题一:没有按照书写方式执行,可读性差 2. 问题二:callback中不易模块化(异步完之后执行的函数,函数不可能特别负责,容易出现耦合度过高的问题) ### 问题解答 1. 单线程就是同时只做一件事,两段js不能同时执行 2. 原因是为了避免dom渲染的冲突 3. 异步是一种“无奈”的解决方案,虽然有很多问题 ### 什么是event-loop(事件循环,事件轮询)(重点) 1. 知识串联 * 单线程--同时间只能做一件事 * 原因:避免dom渲染冲突 * 解决方案-异步 * 实现方式:event-loop 2. event-loop * 事件轮询:js实现异步的具体解决方案 * 同步代码,直接执行 * 异步函数先放在**异步队列**中 * 待同步代码执行完毕,**轮询执行**异步队列的函数(放入主进程中) 轮询执行:异步队列执行第一个函数后,继续监听,当100ms后第二个函数写入异步队列,执行后,继续监听异步队列 ~~~ 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) ~~~ ~~~ //异步队列 //立刻被放入 function(){console.log(2)} //100ms之后被放入 function(){console.log(1)} ~~~ **100ms之后被放入** ~~~ $.ajax({ url:'xxx', url:'./data.json',//可直接请求文件 success:function(result){ console.log(‘a’) } }) setTimeout(function(){ console.log('b'); },100); setTimeout(function(){ console.log('c'); }); console.log('d'); // d c b a // d c a b ~~~ ajax的异步函数是网络请求成功之后,将函数放入异步队列中,事件轮询会立刻执行 ### 回顾 1. 事件轮询,js实现异步的具体解决方案 2. 同步代码,直接执行 3. 异步函数先放在**异步队列**中(如果setTimeout没有延迟时间,直接放入,如果有,需要等延迟时间过后放入异步队列中,ajax等完成之后放入异步队列中) 4. 待同步函数执行完成,**轮询执行 异步队列 **的函数(放入主程序中)(异步队列一直被轮询监听) ### 问题答案 1. 事件轮询,js异步的解决方案 2. 什么是异步队列,何时被放入异步队列 3. 轮询的过程(监听轮询) ***** ### 是否用过jQuery的Deferred(延迟) 不要以为所有的网址都是vue和React开发的 1. jQuery1.5 的变化 2. 使用jQuery Deferred 3. 初步引入Promise概念(deferred演化而来) jquery 1.5之前 ~~~ var ajax = $.ajax({ url:''data.json", success:function(){}, error:function(){} }) console.log(ajax);//这是一个XHR对象 ~~~ 1.5之后 ~~~ var ajax = $.ajax('data.json'); ajax.done(function(){}) .fail(function(){}) .done(function(){});//链式操作 console.log(ajax);//返回一个deferred对象 ~~~ ~~~ //很像Promise的写法 var ajax = $.ajax('data.json'); ajax.then(function(){},function(){})//成功函数和失败函数 .then(function(){},function(){}) ~~~ jQuery早就有了异步解决方案,后来慢慢演化成一个标准(Promise(es6)) ### jQuery1.5的变化 1. 无法改变js异步和单线程的本质 2. 只能从写法上杜绝callback这种形式 3. 它是一种语法糖形式(没改变本质),但是解耦了代码 4. 很好的体现:开发封闭原则(多扩展开发,对修改封闭)(不用修改以前的,只需要进行扩展实现自己的功能)(多人开发不相互干扰,减少回归测试成本) ### 使用jQuery Deferred ~~~ //给出一段非常简单的异步操作代码,使用setTimeout var wait = function(){ var tast = function(){ console.log('完成'); } setTimeout(task,2000); } wait(); //新增需求:要在执行完成之后进行某些特别负责的操作,代码可能会很多,而且分好几个步骤 ~~~ ~~~ 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; } //注意,这里一定有返回值 return wait(dtd);//最终返回dtd } var w = waitHandle(); w.then(function(){},function(){}) w.then(function(){},function(){}) //还有w.done() w.fail() ~~~ 开发封闭原则:对扩展开发,对修改封闭(易于维护,大大减少测试回归工作量) ### 总结 1. dtd的api分为两类,用意不同 2. 第一类:dtd.resolve(成功)dtd.reject(失败)==主动触发 系统主动操作 3. 第二类:dtd.then(被动监听) dtd.done dtd.fail 4. 这**两类应该分开**,否则后果很严重 5. 可以在上面代码最后执行dtd.reject()试一下后果 ### 使用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();//注意,返回的是promise而不是直接返回deferred对象 //return dtd; } //注意,这里一定有返回值 return wait(dtd);//最终返回promise对象 ~~~ return **dtd.promise()**;//注意,**返回的是promise而不是直接返回deferred对象**,会把**resolve和reject过滤掉** ~~~ var w = waitHandle(); w.reject()//报错 $.when(w).then(function(){},function(){}) //因为返回的是promise对象,可能再封装一层($.when(w)) //还有w.done() w.fail() ~~~ ### 问题解答 1. 可以jQuery1.5对ajax的改变举例(deferred怎么用) 2. 说明如何简单的封装,使用Deferred(开发封闭原则说一下好处) 3. 说明**promise和Deferred的区别**(deferred对象有resolve,reject,then,done,fail主动触发函数和被动监听函数,混在一块儿是不行的,容易被篡改,通过生成promise对象进行隔离,**promise对象只能被动监听,不能主动修改**)(这里的promise是ES6的promise的前世) ### Promise的基本使用和原理 1. 基本语法回顾 2. 异常捕获(保证程序正常运行) 3. 多个串联 4. Promise.all和Promise.race 5. Promise标准 ***** 1. Promise语法回顾 ~~~ //Promise function loadImg(src){ var 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="xxxx"; var result = loadImg(src); result.then(function(img){ console.log(img.width) return img;//第二个then才能接受到,不然会undefined },function(){}) .then(function(img){ console.log(img.height) }) ~~~ bootCDN:**bluebird完全支持promise标准**(高级低级浏览器都支持promise) ### 异常捕获 then接收两个参数:成功之后的回调函数,失败之后的回调函数,**进行异常捕获就不需要传两个参数**,**then只接收成功之后的回调函数**,**catch接收异常** ~~~ function loadImg(src){ var promise = new Promise(function(resolve,reject){ var img = document.createElement('img'); //throw new Error('自定义错误') 走catch img.onload = function(){ resolve(img) } img.onerror = function(){ reject(“图片加载失败”);//走catch } img.src = src; }); return promise; } var src="xxxx"; var result = loadImg(src); //规定:then只接受一个参数,最后统一用catch捕获异常 result.then(function(img){ console.log(img.width) }).then(function(img){ console.log(img.height) }).catch(function(ex){ //最后统一catch console.log(ex) }) ~~~ 异常捕获(catch可以捕获的异常错误)(统一的catch收藏起来) 语法报错:throw new Error('自定义错误') 走catch,并不是逻辑之内的(我们要生成的) 逻辑之内的: reject(“图片加载失败”);//走catch 统一处理异常 ~~~ try{}catch(){} ~~~ ### 多个串联 希望****先加载第一个,再加载第二个****,在ajax请求中常有 ~~~ var src1="xxx"; var result1 = loadImg(src1); var src2 = "yyy"; var result2 = loadImg(src2) //链式操作 result1.then(function(img){ console.log('第一个图片加载完成'); return result2;//接下来是result2的.then,不再是result1的then }).then(function(img){ console.log("第二个图片加载完成") }).catch(function(ex){ //最后统一 catch console.log(ex); }) ~~~ 用户登录,先请求随机数和transId,在根据用户信息登录 用户支付:先请求随机数和transId,在根据支付密码登录 编辑商品:同时请求商品信息和分类信息,两个返回先后可能会变 return result2;//非常重要 //接下来是result2的.then,不再是result1的then(不return) ### Promise.all & Promise.race 1. **Promise.all** Promise.all 接收一个promise对象的数组 待全部完成之后,统一执行success 接收到的datas是一个数组,依次包含了多个promise返回的内容 2. **Promise.race** Promise.race接收一个包含多个promise对象的数组,只要一个完成就执行success,data即先执行完成的promise的返回值 ~~~ Promise.all([result1,result2]).then(datas=>{ console.log(data[0]); console.log(data[1]); }) //datas:为数组,即多个promise返回的数据 Promise.race([result1,result2]).then(data=>{ console.log(data); }) ~~~ //肯定race先执行 all后执行 ### Promise标准 1. “标准”:任何技术推广都需要一套标准来支撑(看书,标准的文档)如html js http 等,任何不符合标准的东西,终将会被用户抛弃 2. 状态变化 * 三种状态:pedding,fulfilled,rejected * 初始状态pending * pending变为fulfilled,pending变为rejected * 状态变化不可逆 3. then * Promise实例必须实现then这个方法 * then()必须可以接受两个函数作为参数 * then()返回的必须是一个Promise实例 ~~~ result1.then(function(){ return result2; }).then(function(){ }) ~~~ ### 问题解答 1. 基本语法 2. 如何捕获异常(Error和reject都要考虑) 3. 多个串联-链式执行的好处(否则多层callback) 4. Promise.all和Promise.race 5. Promise标准 - 状态变化,then函数 ***** ### async/await(es7提案中,babel开始支持 nodejs中koa) 用了Promise的特性,并做了一些改进 1. then只是将callback拆分了 2. async/await是直接同步的写法 拆分:本质上还是callback ~~~ var w = waitHandle(); w.then(function(){},function(){}) .then(function(){},function(){}) ~~~ 最直接的同步写法(async/await) ~~~ const load = async function(){ const result1 = await loadImg(src1); console.log(result1); const result2 = await loadImg(src2); console.log(result2); } load(); ~~~ 用法: 1. **使用await,函数必须用async标识** 2. **await后面跟的是一个Promise实例** 3. **需要用babel-polyfill**(兼容)(babel不是默认完全支持的,因为还不是标准) ~~~ var src1 = "xxx" var src2 = "yyy" var self = this; const load = async function(){ var result1 = await self.loadImg(src1); console.log(result1); var result2 =await self.loadImg(src2); console.log(result2); console.log('ttttt')//最后执行 } load(); ~~~ //**同步方法**执行,没有一个回调函数 **npm i --save-dev babel-polyfill** **import "babel-ployfill"** **async function**(){} **await** self.loadImg(src1) (promise实例) **await** self.loadImg(src2)(promise实例) Promise写法虽然是链式操作,但还是要传入一些callback函数的,是异步的写法,async和await是同步写法,没有回调函数了,写法要好很多 ### 问题解答 1. 基本语法(await后加promise实例,函数外层function前加async) 2. 使用了Promise,并没有和Promise冲突(Promise是对异步回调的封装,Promise标准中,封装后要使用的话,要用.then.then.catch等写法,async和await使用了promise的封装,还能使用完全同步的写法) 3. 完全是同步的写法,再也没有回调函数(进步)(并不是取代了promise,而是和promise进行了完毕的兼容)(是promise封装后的扩展) 4. 但是:改变不了**js单线程、异步**的本质 ### 总结 1. 什么是单线程,和异步有什么关系 2. 什么是event-loop 3. 是否用过jquery的Deferred 4. Promise的基本使用和原理 5. 介绍一下async/await 6. 总结一下当前js解决异步的方案 ***** 1. 什么是单线程,和异步有什么关系 * 单线程就是同时只做一件事,两段js不能同时执行 * 原因:为了避免dom渲染的冲突(js单线程,网页渲染单线程,js和网页渲染同属于一个线程) * (单线程现状)异步是一种“无奈”的解决方案,虽然有很多问题(异步非常高效)(问题:回调,写的顺序和执行顺序不一致) * 应对异步的问题的各种解决方案,异步的本质是永远改变不了的,只能从写法上尽量写的顺序和执行顺序一样 2. 什么是event-loop * 事件轮询,JS异步的解决方案(用异步,实际上怎么实现的(解决的)) * 什么是异步队列,何时被放入异步队列(js代码中,同步语句立刻执行,异步的函数要放在异步队列中)(何时放入:setTimeout,根据时间参数过后放入异步队列中,网路请求:ajax或图片被加载完成后,放入队列中,时间不可控) * 轮询的过程:(怎么监控异步队列,并把异步队列的函数执行)(轮询:一遍一遍的查询,监视异步队列,一旦异步队列中有数据,就会立刻拿到主进程中执行,再继续监视) 3. jQuery Deferred * 可以jQuery 1.5对ajax的改变举例(1.5之前,success callback函数,之后可以通过.done .fail .then类似promise)(符合开发封闭原则,对扩展开放,对修改封闭) * 说明如何简单的封装,使用Deferred(setTimeout例子) * 说明promise和Deferred的区别(deferred对象有resolve,reject,then,done,fail主动触发函数和被动监听结果函数,混在一块儿是不行的,容易被篡改,通过生成promise对象进行隔离,promise对象只能被动监听,不能主动修改)(这里的promise是ES6的promise的前世) 4. Promise的使用和原理 * 基本语法 * 如何捕获异常(语法报错,reject) * 多个串联-链式执行的好处(先加载一个,返回之后再加载另一个,有先后顺序) * Promise.all和Promise.race * Promise标准:状态变化、then函数(Promise实例必须实现then函数,接受两个参数,成功和失败的回调函数,**返回一个Promise实例,若没有返回,则默认返回本身执行的实例**,若返回特定的promise实例,则返回) 5. async/await * 基本语法 * 使用了Promise,并没有和Promise冲突(Promise的扩展)(Promise是对异步的封装) * 完全是同步的写法,再也没有回调函数 * 但是,改不了单线程、异步的解决方案 6. 当前异步的解决方案 * jQuery Deferred * Promise(高级浏览器支持 引入bluebird也支持) * Async/await(终极解决方案,依赖于Promise,但同步方法实现) * Generator:原理复杂,和监听器有关,学习成本高,要考虑开发的成本,团队的成本,出错的成本,不是异步的直接代替方式,有更好更简洁的解决方案async/await ,"koa"弃暗投明。