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.异步模式
先说俩概念
整个过程就是,先分任务,同步任务在主线程开始执行,异步任务到任务队列去按照耗时排队,等同步任务做完了,就开始矮个子里拔高个,挨个去将异步任务推入主线程去执行。如此循环往复,子子孙孙,无穷匮也~
想看动态图的,可以看看这个老哥这里
异步操作的常见语法
3.1 事件监听
document.addEventListener('click', function() {
})
3.2 回调函数
相信大家看到回调函数,第一时间想到的就是回调地狱,别怕,这是多么优美的冲锋啊?(联系侵删)
3.2 promise
二话不说,先用起来!
// 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 )
别的不谈,有微任务先执行微任务,再执行宏任务,所以,现在上面的面试题会做了吗?
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)
}
参考
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!