## 异步
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"弃暗投明。
- 空白目录
- 双樾
- JS基础知识
- JS-WEB-API
- 开发环境
- 运行环境
- ES6
- 原型
- 异步
- 虚拟dom
- mvvm
- 组件化和React
- hybrid
- 其他
- 补充
- 技巧
- 快乐动起来呀
- css
- 掘金小册子
- js基础知识
- ES6知识点
- JS异步
- JS进阶知识
- 思考题
- DevTools Tips
- 浏览器基础知识
- 浏览器缓存机制0
- 浏览器渲染原理
- 安全防范知识点0
- 从V8中看JS性能优化0
- 性能优化琐碎事
- Webpack性能优化0
- 实现小型打包工具0
- React和Vue
- Vue生命周期
- vue基础知识点
- Vue响应式
- vue高级
- React基础
- Vue.js技术解密
- 准备工作
- 数据驱动
- new Vue()
- vue实例挂载
- 组件化
- 深入响应式原理
- 编译
- 扩展
- Vue Router
- Vuex