javascript考点高级 —— 异步
一、什么是单线程,和异步的关系
- 单线程,只有一个线程,只能做一件事
- 原因,避免DOM渲染的冲突
- 解决方案异步
1、看如下单线程例子:
//循环运行期间,JS执行和DOM渲染暂时卡顿
var i, sum = 0;
for(i = 0; i < 1000000000; i++){
sum += i
}
console.log(sum)
//alert不处理,JS执行和DOM渲染暂时卡顿
console.log(1)
alert('hello')
console.log(2)
2、为什么会有单线程,原因是避免DOM渲染冲突
浏览器需要渲染DOM,JS可以修改DOM结构,JS执行的时候,浏览器DOM渲染会暂停,两段JS也不能同时执行(都修改DOM冲突)。JS执行的时候和浏览器渲染DOM在一个线程中,否则会出现DOM渲染冲突。HTML5中的webworker支持多线程,但是不能访问DOM。
3、解决单线程的办法——异步
(1)先看下面异步的例子:
console.log(100)
setTimeout(function() {
console.log(200)
})
console.log(300)
console.log(400)
console.log(100)
$ajax({
url:'xxxxxx',
success: function(result) {
console.log(result)
}
})
console.log(300)
console.log(400)
(2)异步的一些问题:
- 没按照书写的方式执行,可读性差
- callback中不容易模块化
二、什么是event-loop
event-loop就是事件轮询,JS实现异步的具体解决方案(原理)。具体执行过程:(1)同步代码,直接执行。(2)异步函数先放在异步队列中。(3)待同步函数执行完毕,轮询执行异步队列的函数。
1、实例分析
在上述的图里面,第一个setTimeout会立即放入异步队列,第二个setTimeout会100ms之后被放入异步队列中,然后主进程执行结束后,就去执行异步队列,当主进程执行完,有可能第二个setTimeout还没有放入异步队列中,这时JS搜索引擎就会轮询异步队列,如果有就将其执行。
3、轮询是什么意思
- 浏览器会监视异步队列,只要异步队列里面有东西,就立刻拿去执行,然后又继续监视异步队列。
三、jQuery的Deferred
1、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('success1')
})
console.log(ajax) //返回了一个deferred
//第二种写法
var ajax = $.ajax('data.json')
ajax.then(function(){
console.log('success1')
}, function(){
console.log('error1')
})
.then(function(){
console.log('success2')
}, function(){
console.log('error2')
})
jQuery1.5的变化,但是无法改变JS异步和单线程的本质,只能从写法上杜绝callback这种形式,它是一种语法糖式,但是解耦了代码,很好的体现了开放封闭原则。
2、jQuery Deferred
jQuery1.5的变化在jQuery中添加了Deferred,之前是没有的。然后jQuery Deferred的语法就没有变过。
(1)先看如下的例子:
var wait = function() {
var task = function(){
console.log('执行完成')
}
setTimeout(task, 2000)
}
wait()
现在假设我们执行之后进行某些特别复杂的操作,代码可能会很多,而且分好几个步骤,我们该怎么做?
(2)在来看如下的例子:
function waitHandle() {
var dtd = $.Deferred() //创建一个deferred对象
var wait = function(dtd) {
var task = function() { //要求传入一个eferred对象
console.log('执行完成')
dtd.resolve() //表示异步任务已经完成
//dtd.reject() //表示异步任务失败或出错,如果执行reject,后面不能使用链式操作
}
setTimeout(task, 2000)
return dtd //要求返回一个deferred对象
}
return wait(dtd) //这里一定要有返回值
}
var w = waitHandle()
//w.reject() //这样使用很不规范,下面输出结果是error 1 error 2
w.then(function() {
console.log('ok 1')
}, function(){
console.log('error 1')
}).then(function() {
console.log('ok 1')
}, function() {
console.log('error 2')
})
//还有w.done w.fail
dtd的API可分为两种,用意不同,第一类(主动执行):dtd.resolve dtd.reject。第二类(被动监听):dtd.then dtd.done dtd.fail。这两个类必须分开,否则后果很严重。为了解决外部使用主动执行的第一类API,我们引出了promise。
function waitHandle() {
var dtd = $.Deferred()
var wait = function(dtd) {
var task = function() {
console.log('执行完成')
dtd.resolve()
//dtd.reject()
}
setTimeout(task, 2000)
return dtd.promise //这里就是解决办法
}
return wait(dtd) //这里一定要有返回值
}
var w = waitHandle() //经过上面的改动,w接受的就是一个promise对象
$.when(w)
.then(function(){
console.lof('ok 1')
})
.then(function(){
cinsole.log('ok 2')
})
//这里执行w.reject()这句话会直接报错
3、关于defrred问题,怎么回答,可以从以下几个方面回答
- 可以对jQuery1.5之前和之后的ajax进行对比
- 说明如何简单的封装,使用Deferred
- 说明promise和Defrred的区别
四、Promise的基本原理
1、Promise的基本语法回顾
2、异常捕获
function loadImg(src) {
const promise = new Promise(function (resolve, reject) {
var img = document.createElement('img')
img.onload = function() {
resolve(img)
}
img.onerror = function() {
reject('图片加载失败') //为了让catch一起捕获逻辑之外的语法异常,我们传了一个参数 ‘图片加载失败’
}
img.src = src
})
return promise
}
var src = 'http://www.imoooc.com/static/img/index/logo_new.png'
var result = loadImg(src)
//then只接受一个参数,请求成功的函数,最后统一用catch捕获异常
result.then(function(img){
console.log(img.width)
return img
}).then(function(img){
console.log(img.height)
}).catch(function(ex){
console.log(ex)
})
3、多个串联
var src1 = '.........'
var result1 = loadImg(src1)
var src2 = '..........'
var result2 = loadImg(src2)
//链式操作
result1.then(function(img1){
console.log('第一个图片加载完成')
return result2
}).then(function(img2){
console.log('第二张图片加载完成')
}).catch(function(ex){
console.log(ex)
})
4、promise-all-race
Promise.all接受一个promise对象数组,Promise.race接受一个包含多个promise对象的数组。
//这里的result1和result2就是上面代码中的result1和result2
//待全部完成之后,统一执行success
Promise.all([result1, result2]).then(datas => {
//接收到的datas是一个数组,依次包含多个promise返回的内容
console.log(datas[0])
console.log(datas[1])
})
//只要完成一个,就执行success
Promise.race([result1, result2]).then(data =>{
//data即最先执行完成的promise的返回值
console.log(data)
})
5、Promise标准
(1)标准
任何技术推广都需要一套标准来支撑。任何不符合标准的东西,终将会被用户抛弃。不要挑战标准,不要自造标准。
(2)状态变化
- 三种状态:pending fulfilled rejected
- 初始状态就是pending
- pending可以变成fulfilled,pending可以变为rejected
- 状态不可逆
(3)then
- Promise实例必须实现then发方法
- then()必须接受两个函数作为参数
- then()返回的必须是一个Promise实例(如果Promise的then()没有铭文的返回实例,那么实例就是then()方法前面的本身的实例,如果返回了,后面的而执行就是返回的实例)
五、async/await(ES7提案)
then只是将callback拆分了(看前面的例子),但是还是异步的写法。async/await是最直接的同步写法。
1、async/await的语法:
//同步的写法
const load = async function(){
const result1 = await loadImg(src1) //前面代码中的src1
console.log(result1)
const result2 = await loadImg(src2) //前面代码中的src2
console.log(result2)
}
load()
2、用法
- 使用await,函数必须用async标识
- await后面跟的是一个Promise实例
- 需要用babel-polyfill
3、问题解答
- 使用了Promise,但没有和Prromise冲突
- 完全是同步写法,没有回调函数(它和Promise做了完美的结合)
- 但是:改变不了JS单线程、异步的本质
六、总结
- 什么是单线程,和异步有什么关系
解答:单线程就是同时只做一件事情,不能同时执行两段JS代码。原因是避免DOM渲染冲突。然后就出现了异步,异步是一种“无奈”的解决方案,虽然有很多问题。
- 什么是event-loop
解答:就是事件轮询,JS异步的解决方案。什么是异步队列,何时被放入异步队列(没有时间参数,立即放入。有时间参数,等待时间过后,加入异步队列。有请求时,请求成功放入异步队列)。轮询的过程。
- 是否用过jQuery的Deferred
解答:对比jQuery1.5前后的ajax。说明如何简单的封装、使用Deferred。说明promise和Deferred的区别。
- Promise的基本使用和原理
解答:基本语法。如何捕获异常。多个串联-链式执行的好处。Promise.all和Promise.race。Promise的标准-状态变化、then函数。
- 介绍一下async/await(和Promise的区别)
解答:使用了Promise,但是没有和Primise冲突。完全同步的写法,没有回调函数。
- 总结一下当前JS解决异步的方案
解答:(1)jQuery Defrred。(2)Promise。(3)Async/Await。(4)Genterator(并不是异步的直接替代方式,解决异步需要很复杂的封装)
推荐阅读
-
JavaScript系列之―同步还是异步?
-
突袭HTML5之Javascript API扩展1—Web Worker异步执行及相关概述
-
使用jQuery异步加载 JavaScript脚本解决方案
-
Javascript 高级手势使用介绍
-
浅析JavaScript异步代码优化
-
Javascript vue.js表格分页,ajax异步加载数据
-
javascript高级选择器querySelector和querySelectorAll全面解析
-
javaScript高级程序设计:正则表达式代码实例
-
JavaScript的同步和异步编程实例讲解
-
JavaScript高级程序设计第五章引用类型——RegExp类型