欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

javascript考点高级 —— 异步

程序员文章站 2022-05-08 16:18:35
...

一、什么是单线程,和异步的关系

  • 单线程,只有一个线程,只能做一件事
  • 原因,避免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、实例分析

javascript考点高级 —— 异步

javascript考点高级 —— 异步

 在上述的图里面,第一个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(并不是异步的直接替代方式,解决异步需要很复杂的封装)