针要学前端 | JavaScript深度挖掘之异步编程

在这里插入图片描述

大家好,我是指针。冬天到了,人也变懒了,为了让自己动起来,我报名参加了拉勾教育的大前端高薪训练营。学习需要总结,需要分享,需要鞭策,于是便有了《针爱学前端》这一系列,希望大家看完能够有收获。如果文章中有不对的地方,希望能批评指正,不吝赐教!!!

JavaScript异步编程

0.先做题,醒醒脑子,成功的请直接跳转到参考,失败的按照顺序往下看
console.log(1)
setTimeout(()=> {
    console.log(2)
}, 0)
new Promise(res => {
    console.log(3)
    setTimeout(()=> {
        console.log(4)
        res()
    },0)
}).then(()=> {
    console.log(5)
})
console.log(6)
1.概念叨叨叨,让异步等一会儿

众所周知JavaScript是单线程的,当初设计出来就是运行在浏览器上的脚本语言,浏览器最重要的操作就是Dom操作,而为了避免多个线程同时修改一个Dom,造成冲突,JavaScript就采用了单线程。

优点:中华人民共和国46年,JavaScript上任浏览器,拿着喇叭大喊,我来浏览器就为三件事:安全!安全!还是TMD的安全!!!

但是!!!剿匪需要时间,打黄老爷需要时间,老百姓还在等着呢!可惜了,咱兄弟一直是一起行动的(JS执行环境中负责执行代码的线程只有一个),现在耗时的事儿太多堵塞了呀。不过张麻子聪明啊,咱们兄弟这么多人分成俩队,一个叫同步模式队,一个叫异步模式队。不耗时的活比如贴大字报(console.log())由同步模式队做,耗时的比如赴宴、喝酒、调兵(延时,ajax)就让异步模式队干。等同步模式队干完了,异步模式队再按照时间长短把活儿给干了,先剿匪,再杀黄四郎,咱一定还浏览器一个朗朗乾坤。

“大哥,异步还没做,在排队”。

“不急,让异步等一会儿!”

2.同步模式

同步模式指的不是一起执行,而是依次执行,一件事干完,下一件事儿上,执行顺序与代码编写顺序一致。具体的不说了,懂得都懂😀

3.异步模式

先说俩概念

事件循环(event loop):

  1. 同步事件入栈,异步事件在环境线程中等待执行
  2. 同步事件执行完出栈,直到执行完。异步事件触发时,往队列中插一条消息
  3. 读取消息队列的第一条消息,相对于的异步事件入栈
  4. 执行栈空了后再拉取消息队列消息,这就是一次事件循环

消息队列(event queue):消息队列是存储异步任务消息的,当异步任务触发时,会往消息队列插入一条消息,等执行栈空了,就会读取消息队列的消息,读取消息时,对应的异步任务会入栈执行

整个过程就是,先分任务,同步任务在主线程开始执行,异步任务到任务队列去按照耗时排队,等同步任务做完了,就开始矮个子里拔高个,挨个去将异步任务推入主线程去执行。如此循环往复,子子孙孙,无穷匮也~

想看动态图的,可以看看这个老哥这里

异步操作的常见语法

3.1 事件监听
document.addEventListener('click', function() {
    
})
3.2 回调函数

相信大家看到回调函数,第一时间想到的就是回调地狱,别怕,这是多么优美的冲锋啊😀(联系侵删)

在这里插入图片描述

3.2 promise

promise承诺一个状态,初始值为pending,已兑现=>fullfilled,已拒绝=>rejected,一但状态从pending发生改变,这个状态便会锁定,不可更改。

二话不说,先用起来!

// Promise是一个对象,接受一个函数作为参数
// 这个函数接受两个参数,一个resolve 成功回调,一个reject 失败回调
// 一个promise只能在成功或失败中保持一个状态,且不可更改
// 成功回调在.then中触发,失败回调在.catch中触发
let p = new Promise((resolve, reject) => {
    // 使用定时器模拟接口调用
    setTimeout(()=> {
        let randonNum = Math.random()
        if( randonNum > 0.5) {
            resolve(randonNum)
        } else {
            reject(randonNum)
        }
    }, 1000)
})

p.then((value) => {
    console.log(value)
}).catch((value) => {
    console.log(value)
})

当然,Promise的链式调用才是解决回调地狱的秘密武器

let count = 1
// 每次调用,count++
let promiseAjax = function () {
    return new Promise((res, rej) => {
        setTimeout(()=> {
            count++
            res(count)
        }, 1000 * count)
    })
}
let p = promiseAjax()
p.then(value => {
    console.log(value)
    return promiseAjax()
}).then(value => {
    console.log(value)
    return promiseAjax()
}).then(value => {
    console.log(value)
    return promiseAjax()
}).then(value => {
    console.log(value)
    return promiseAjax()
})

Promise的静态函数中,有四个可以记一下

  • Promise.all([]) 接受一堆Promise,并行处理,全部成功才成功,返回一个新的Promise
  • Promise.race([]) 也是接受一堆Promise,看谁跑得快,谁快就用谁的,一个结束,就结束,可以用于接口超时处理
  • Promise.resolve() 返回一个状态由给定value决定的Promise对象。如果该值是thenable(即,带有then方法的对象),返回的Promise对象的最终状态由then方法执行决定;否则的话(该value为空,基本类型或者不带then方法的对象),返回的Promise对象状态为fulfilled,并且将该value传递给对应的then方法。通常而言,如果您不知道一个值是否是Promise对象,使用Promise.resolve(value) 来返回一个Promise对象,这样就能将该value以Promise对象形式使用。
  • Promise.reject() 返回一个状态为失败的Promise对象,并将给定的失败信息传递给对应的处理方法

宏任务与微任务

同为异步,也分两种,这就是宏任务(macrotask )与微任务(microtask )
宏任务是由JS的执行环境来发起的,而微任务时JS自身发起的

宏任务包括:setTimeout,setInterval,requestAnimationFrame

微任务包括:Promise.then .catch .finally,process.nextTick,MutationObserver

别的不谈,有微任务先执行微任务,再执行宏任务,所以,现在上面的面试题会做了吗?

3.4 generator

Promise的链式调用,你链多了,看着也膈应,所以es6中还有一个生成器函数(generator)

generator是一个可以“返回”多次的“函数”,这样就可以保存每一次的值

二话不说,先用起来

function * foo () {
    console.log("start")
    let r1 = yield promiseAjax()
    console.log(r1)
    let r2 = yield promiseAjax()
    console.log(r2)
}
// foo并不会执行,只会生成一个generator对象
// 需要调用g的next方法,函数才会向下执行,并且执行到yield处停止
// g.next()返回一个对象 {value: x, done: true/false},其中value是Promise reuturn的值
// done代表着generator的执行状态,true表示执行结束,false表示没有执行结束
// yield只会暂停函数执行,并不是return出去,当下一次next方法执行时,函数又会接着往下执行
let g = foo()
console.log(g)
let result = g.next()
result.value.then(data => {
    // 将data赋值给r1,并且开始下一次执行,执行到下一个yield
    g.next(data)
})

generator函数的执行吧,如果你想执行到底,你得一直next()下去,但是这样一直写太呆了,我们给它封装成一个递归函数

function co (generator) {
    let g = generator()
    function handleResult (result) {
        if (result.done) return
        result.value.then(data => {
            handleResult(g.next(data))
        })
    }
    handleResult(g.next())
}
3.5 async/await

是不是觉得generator写的方式很蛋疼?没关系,之前蛋疼并且爱动脑子的家伙已经为我们准备好了generator的语法糖,用法极其简单,写法也是同步的写法,堪称完美

async function foo () {
    let r1 = await asyncFn1()
    let r2 = await asyncFn2()
    let r3 = await asyncFn3()
    console.log(r3)
}

导航

针要学前端 | JavaScript深度挖掘之函数式编程
针要学前端 | JavaScript深度挖掘之异步编程
针要学前端 | JavaScript深度挖掘之手写Promise
针要学前端 | JavaScript深度挖掘之ECMAScript

参考

以上皆由拉勾教育大前端训练营提供材料😀

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值