前端异步代码烧脑的原因
Posted yangsg
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了前端异步代码烧脑的原因相关的知识,希望对你有一定的参考价值。
同步与异步
同步:等待结果
异步:不等待结果
注意,异步常常伴随回调一起出现,但是异步不是回调,回调也不一定是异步
如下代码就是同步,只要fn内部的循环完毕才能执行下面的代码
function fn(){ for(var i=0;i<5000000000;i++){ } } var startTime = new Date().getTime(); console.log(‘开始了‘) fn(); var exeTime = new Date().getTime() - startTime; console.log(‘执行了‘+exeTime/1000+‘秒‘)
异步代码
- 定时器就相当于浏览器设置了一个闹钟
- 之后继续执行
- 闹钟到了时间再去执行相应的代码
//异步的 asyncFn function asyncFn(fn){ setTimeout(fn, 3000) } console.log(1) asyncFn(()=> console.log(‘wake up‘)) console.log(2)
前端经常遇到的异步
//因为图片还没有加载完毕所以并不知道高度,(请禁用浏览器缓存在执行) document.getElementsByTagNames(‘img‘)[0].width // 宽度为 0 console.log(‘done‘)
你应该在图片加载成功后在获取图片的高度
document.getElementsByTagNames(‘img‘)[0].onload = function(){ console.log(this.width) // 宽度不为 0 console.log(‘real done‘) } console.log(‘done‘)
面试题中的异步
为啥点击li每次都打印 6
<li>11111111</li> <li>22222222</li> <li>33333333</li> <li>44444444</li> <li>55555555</li> <li>66666666</li> ...... let liList = document.querySelectorAll(‘li‘) for(var i=0; i<liList.length; i++){ liList[i].onclick = function(){ console.log(i) } } console.log(i) //6
- for循环用极短的时间执行完毕 我们假设 3 毫秒
- var i 有变量提升 即使循环之后仍然可以访问到i的值
- 用户看到 Li 的时间绝对大于3 毫秒,每个 Li 点击事件已经初始化结束
- log(i) 还是外面的 i
如何解决 把 var i 改为 let i 就可以了
AJAX 中的异步
这样调用虽然是同步的,但是页面会失去响应,直到结果回来
let request = $.ajax({ url: ‘.‘, async: false }) console.log(request.responseText)
//异步方式 $.ajax({ url: ‘/‘, async: true, success: function(responseText){ console.log(responseText) } })
异步的形式
一般有两种方式拿到异步结果
傻逼方法:轮训
老板叫你去买土豆,然后他就隔1分钟问一次买到土豆了吗?
正规方法:回调
老板叫你去买土豆并告诉你回来之后通知他,20分钟后你回来了告诉老板是否买到了土豆
回调的形式
Node.js 的 error-first 形式
fs.readFile(‘./1.txt‘, (error, content)=>{ if(error){ // 失败 }else{ // 成功 } })
jQuery 的 success / error 形式
$.ajax({ url:‘/xxx‘, success:()=>{}, error: ()=>{} })
jQuery 的 done / fail / always 形式
$.ajax({ url:‘/xxx‘, }).done( ()=>{} ).fail( ()=>{} ).always( ()=> {})
Prosmise 的 then 形式
//成功调第一个函数,失败调用第二个函数 $.ajax({ url:‘/xxx‘, }).then( ()=>{}, ()=>{} ).then( ()=>{})
- ajax的 Prosmise跟真正的Promise有点不同
- 它也可以一直.then().then().then() 这样链式操作
Promise只是一种回调的形式
Promise规范
axios({ url:‘xxx‘ }).then(s1,e1) .then(s2,e2) .then(s3,e3)
如果是jquery,成功只会走进成功的分支
s1 ==> s2 ==> s3 e1 ==> e2 ==> e3
Promise规范是责任制
s1 e1 是第一责任人
s2 e2 是第二责任人
s3 e3 是第三责任人
axios({ url:‘/aaa‘ }).then(()=>{ console.log(‘成功1‘) alert(xxxxx) //xxxxx 没有定义 },()=>{ console.log(‘失败1‘) }).then(()=>{ console.log(‘成功2‘) },()=>{ console.log(‘失败2‘) })
- s1 执行过程不够顺利报错了(就会产生错误的结果)
- 于是第二责任人接收到的是一个烂尾工程
- 当做错误处理 走进e2
如果没有 e2 就会报错到浏览器显示给开发者
两种写法
axios({ url:‘/aaa‘ }).then(()=>{ console.log(‘成功1‘) alert(xxxxx) //xxxxx 没有定义 },()=>{ console.log(‘失败1‘) }).then(()=>{ console.log(‘成功2‘) }).catch((err)=>{ console.log(err) }) axios({ url:‘/aaa‘ }).then(()=>{ console.log(‘成功1‘) alert(xxxxx) //xxxxx 没有定义 },()=>{ console.log(‘失败1‘) }).then(()=>{ console.log(‘成功2‘) }).then(undefined,(err)=>{ console.log(err) })
自己返回 Promise
function buyApple(){ var fn =(succ,err)=>{ var rnd = Math.random() setTimeout(()=>{ if(rnd>0.5){ succ(‘买到了苹果‘) }else{ err(‘没买到苹果‘) } },2000) } return new Promise(fn) //fn.call(undefined,success,err) } var promise = buyApple(); promise.then( (data)=>{console.log(data)}, (err)=>{console.log(err)} )
function ajax(){ return new Promise((resolve, reject)=>{ 做事 如果成功就调用 resolve 如果失败就调用 reject }) } var promise = ajax() promise.then(successFn, errorFn)
Promise 深入阅读:http://www.cnblogs.com/hustskyking/p/promise.html
Promise/A+ 规范:https://segmentfault.com/a/1190000002452115
async / await
await
function buyApple(){ var fn =(succ,err)=>{ var rnd = Math.random() setTimeout(()=>{ if(rnd>0.5){ succ(‘买到了苹果‘) }else{ err(‘没买到苹果‘) } },10000) } return new Promise(fn) //fn.call(undefined,success,err) } var result = await buyApple(); console.log(2)
10秒之后返回买苹果的结果(成功/失败)
打印2
- await相当于 用异步的方式写同步的代码
- 直到异步代码结果回来才把值赋给 result
await的用法就是 后面接一个返回Promise对象的函数
async
function buyApple(){ var fn =(succ,err)=>{ var rnd = Math.random() setTimeout(()=>{ if(rnd>0.5){ succ(‘买到了苹果‘) }else{ err(‘没买到苹果‘) } },10000) } return new Promise(fn) //fn.call(undefined,success,err) } async function fn(){ var result = await buyApple() return result } var r = await fn().then( (data)=>{console.log(data)}, (err)=>{console.log(err)}) console.log(1)
分析1
var r = await fn().then( (data)=>{console.log(data)}, (err)=>{console.log(err)}) console.log(2) /* 1.会直接打印2 2. 10秒后打印 买苹果的结果 */
分析2
var r = await fn().then( (data)=>{console.log(data)}, (err)=>{console.log(err)}) console.log(2) /* 10秒后打印 买苹果的结果 然后打印2 */
以上是关于前端异步代码烧脑的原因的主要内容,如果未能解决你的问题,请参考以下文章