导读
异步的相关内容也可结合AJAX文章中的异步与回调。异步常常伴随着回调一起出现,但是异步不是回调,回调也不一定是异步。
之所以能实现异步,是因为把这件事情交给了浏览器来做
// 同步的sleep function sleep(seconds){ var start=new Date() while(new Date() - start < seconds * 1000){ //保证js在这3秒内什么也不执行,只需要js来回循环看看有没有到3秒, // 因为js本身是不是支持原地等待的,所以用while } return } console.log(1) sleep(3) console.log('zzl') console.log(2) //结果是 1 zzl 2 ////////////////////////////////////////////// //异步sleep function sleep(seconds,fn){ //会设置一个闹钟,即在3秒后调用fn,3秒内js什么也不执行,js也不用来回看有没有到3秒, //而是浏览器会在到时间后调用这个fn,而不是JS调用了 setTimeout(fun,seconds*1000) } console.log(1) //打印出1 sleep(3,()=>console.log('zzl')) // js会马上执行下面的代码,而不是等待,因为sleep是在让浏览器做事情, //而js很空闲的,所以设置完闹钟就会立马输出2 console.log(2) //结果是 1 2 zzl
前端经常遇见的异步
图片加载时需要时间
var w = document.getElementsByTagName('img').[0].width //宽度为0 console.log(w)
最后宽度为0,并没有拿到图片,因为去下载图片的时候,就已经去获取img的宽高了,但是由于第一次没有缓存,也就没有下载好图片,故为0,
解决办法
var imgw = document.getElementsByTagName('img').[0].width //监听一个事件,图片下载成功了,我再去拿宽度 //挂一个函数放在onload身上,等你加载成功了就去调用我这个函数(即回调) imgload.onload=function(){ var w = imgw.width console.log(w) //此时能拿到图片宽度 } console.log(imgw.width) //依旧拿不到宽度
面试题中的异步
let liList = document.querySelectorAll('li') //此时下面的var i=0运行时会形式上的放在外面,变成for外部的变量了(俗称变量提升) //即var i //for(i=0;i<;i++){} for(var i=0;i<liList.length;i++){ //所以此时没有点击的时候,for已经循环判断结束了,也就是i=6 liList[i].onclick=function(){//浏览器并不会等这个异步函数执行,直接进入下一个循环 console.log(i) //只有点击了才回输出 } } 结果点击后只输出6
解决办法
let liList = document.querySelectorAll('li') // 用let可以解决,因为let不会被提升到外面去, // let的作用域就仅仅在这个for循环范围内 for(let i=0;i<liList.length;i++){//这是for的i,但是每循环一次,就会分身一个此时的i值给下面 liList[i].onclick=function(){ //都有一个自己的i,即上面产生的分身i console.log(i) //打印的是我自己的i } }
AJAX中的异步
let request=$.ajax({//这个函数会一直等这个请求回来,等请求完成后再赋值给request url:'.',//获取当前url页面 async=false }) //此时上面的请求会一直占用着时间,停在了那里,整个页面什么也不能操作 console.log(request.responseText)//打印当前页面
异步解决办法
$.ajax({ url:'.', async=true,//用异步的ajax //告诉浏览器,当请求返回的时候就调用一下我这个success函数 //当浏览器通知请求已经返回的时候,浏览器也会顺便把请求的结果response传给我这个函数 success:function(response){ console.log(response)//请求的结果 } }) console.log('请求发送完毕') //此时页面在等请求的同时也可以照样操作 //然后才会输出请求的结果
Promise
- 也是回调,但是不需要记success和fail
- then的第一个参数就是success
- then的第二个参数就是fail
熟记这个格式
return new Promise((resolve,reject)=>{})
function ajax(){ return new Promise((resolve,reject)=>{ //如果成功就调用resolve //如果失败就调用reject }) } var promise = ajax() promise.then(success,err)
简单总结
让一个回调的异步函数变成promise的异步函数的步骤
- return new Promise((resolve,reject)=>{…})
- 任务成功就调用resolve(result)
- 任务失败就调用reject(error)
- resolve和reject会去调用成功和失败函数
- 使用的时候就直接 .then(success,fail)传入成功和失败函数
- ….
await(es8)
//只有等fn被执行完了(成功或者失败)才会把值赋给promise,才会执行下下一行代码 //相当于这个等于号是一个异步的等于号, var promise = await fn() //await必须接一个返回promise的函数
await必须接一个返回promise的函数,然后把这个promist类型简化成普通对象
async
function fn(){ var result=await fn2() //等fn2执行完再赋值给result return result } var s=fn() //会报错,异步函数不能用于同步等于号,因为js不知道fn这个是不是异步函数 // 修改后//////////////////////////////////////////////// //创建promise对象 const fn2 = new Promise((resolve,reject)=>{ resolve('成功了'); //reject('失败啦'); }) //async声明这个函数就是异步函数,返回的是一个promise对象 async function fn(){ var result=await fn2 //await跟promise对象。等fn2执行完 return result } var s=await fn() //此时我会等我已经知道的这个异步函数执行完的时候再去执行下一行代码
推荐去看阮一峰的es6里讲的async/await 内容,讲解非常详细。