异步
什么是异步?什么是同步?
同步——直接拿到结果
异步——不能直接拿到结果
异步举例
以AJAX为例
request.send()之后,并不能直接得到response,必须等到readyState变为4后,浏览器回头调用request.onreadystatechange函数,才能得到request.response。

回调 callback
写给自己用的函数,不是回调
写给别人用的函数,就是回调
request.onreadystatechange就是写给浏览器调用的,意思是浏览器回头调用一下这个函数。
回调举例
-
把函数1给另一个函数2
function f1(){} function f2(fn){ fn() } f2(f1) 复制代码 -
分析
没有调用f1,但是把f1传给了f2,f2调用了f1,f1是写给f2调用的函数,f1就是回调。
-
抬杠
function f1(x){ console.log(x) } function f2(fn){ fn('你好') } f2(f1) 复制代码f1怎么会有一个x参数?
fn('你好')中的fn就是f1,'你好'会被赋值给参数x
异步和回调的关系
关联
异步任务需要在得到结果时通知JS来拿结果,可以让JS留一个函数地址给浏览器,异步任务完成时,浏览器调用该函数地址即可,同时把结果作为参数传给该函数,这个函数是写给浏览器调用的,所以是回调函数。
区别
异步任务需要用到回调函数来通知结果,但回调函数不一定只用在异步任务里
回调也可以用到同步任务里——
array.forEach(n=>console.log(n))就是同步回调
判断同步异步
判断方法
如果一个函数的返回值处于下列三种情况,那么这个函数就是异步函数
setTimeout
AJAX //(即XMLHttpRequest)
AddEventListener
复制代码
没错,AJAX可以设置成同步,但是没必要,这样做会使请求期间页面卡住。
举个例子
摇骰子
function 摇骰子(){
setTimeout(()=>{
return parseInt(Math.random()*6) + 1 //随意1-6
},1000)
//return undefined
}
复制代码
分析
摇骰子()没有写return,那就是return undefined
箭头函数里有return,返回真正的结果
所以这是一个异步函数/异步任务
摇骰子优化——回调
function f1(x){console.log(x)} //利用回调,将摇骰子函数得到的结果作为参数传给f1
function 摇骰子(fn){
setTimeout(() => {
fn(parseInt(Math.random()*6) + 1)
},1000)
}
复制代码
摇骰子——简化箭头函数
由于f1声明之后只用了一次,所以可以删掉f1
function f1(x){console.log(x)}
摇骰子(f1)
复制代码
改为
摇骰子(x=>{console.log(x)})
复制代码
再简化为
摇骰子(console.log) //如果参数个数不一致,就不能这样简化,比如下面这个面试题
复制代码
面试题
还原这个完整的代码
parseInt(string,radix)解析一个字符串并返回指定基数的十进制整数
const array = ['1','2','3'].map((item,i,arr)=>{ return parseInt(item,i,arr) // parseInt('1',0,arr) => 1 //传入0是无效参数,直接传出1 // parseInt('2',1,arr) => NaN //传入1代表1进制,1进制里只有0啊,所以只能得出NaN // parseInt('3',2,arr) => NaN //传入2代表2进制,2进制只有0和1,没有3,传出NaN }) 复制代码这样写才可以得到想要的答案
简写的话可以写成
Promise的用法
如果异步任务有两个结果——成功或失败,怎么办?
方法1——回调接受两个参数
fs.readFile('./1.txt',(error,data)=>{
if(error){console.log('失败');return}
console.log(data.toString()) //成功
})
复制代码
方法2——使用两个回调
ajax('get','/1.json',data=>{},error=>{})
//一个成功回调函数,一个失败回调函数
ajax('get','/1.json',{
success: ()=>{}, fail: ()=>{}
})
//接受一个对象,对象有两个key表示成功和失败
复制代码
以上方法有些缺点:
-
不规范,名称五花八门,没有成文规定。有人用success+error,有人用success+fail,有人用done+fail。
-
容易出现回调地狱,代码变得看不懂
例如
getUser(user => { getGroups(user.(groups)=>{ groups.forEach((g)=>{ g.filter(x => x.ownerId === user.id) .forEach(x => console.log(x)) }) }) }) 复制代码 -
很难进行错误处理
怎么解决回调问题?
- 规范回调的名字或顺序
- 拒绝回调地域,让代码可读性更强
- 很方便地捕获错误
Promise
1976年,Daniel P.Friedman 和 David Wise 提出Promise思想
后人基于此发明了Future、Delay、Deferred等
前端结合Promise 和 JS,制定了Promise/A+规范
该规范详细描述了Promise的原理和使用方法
以AJAX的封装为例
ajax = (method,url,optiopns) => {
const {success,fail} = options //析构赋值
const request = new XMLHttpRequest()
request.open(method,url)
request.onreadystatechange = () => {
if(request.readyState === 4){ //成功就调用 success,失败就调用fail
if(request.status < 400){
success.call(null,request.response)
}else if(request.status >= 400){
fail.call(null,request,request.status)
}
}
}
request.send()
}
ajax('get','/xxx',{
success(response){}, fail:(request,status) =>{}
}) //左边success是function缩写,右边是箭头函数
复制代码
改成Promise写法
ajax = (method,url,options) => {
return new Promise((resolve,reject)=>{
const {sucess,fail} = options
cosnt request = new XMLHttpRequest()
request.open(method,url)
request.onreadystatechange = () =>{
if(request.readyState === 4){
//成功就调用resolve,失败就调用reject
if(request.status < 400){
resolve.call(null,request.response)
}else if(request.status >= 400){
reject.call(null,request)
}
}
}
request.send()
})
}
ajax('get','/xxx')
.then((response)=>{}, (request,status)=>{})
//虽然改写也是回调,但是不需要记success和fail
//then 的第一个参数就是success, then 的第二个参数就是fail
//ajax()返回了一个含有 .then()方法的对象
复制代码
两步走——
-
return new Promise((resolve,reject)=>{...})任务成功则调用
resolve(result)任务失败则调用
result(error)resolve和reject会再去调用成功和失败函数 -
使用
.then(success,fail)传入成功和失败函数
上面封装ajax的缺点——
-
post无法上传数据
request.send(这里可以上传数据) -
不能设置请求头
request.setRequestHeader(key,value) -
怎么解决呢?
继续完善ajax
使用
jQuery.ajax使用
axios
jQuery.ajax
优点:
支持更多形式的参数
支持Promise
支持更多功能
axios
目前最新的AJAX库
代码示例
axios.get('5.json')
.then(response =>
console.log(response)
)
复制代码
高级用法——
-
JSON自动处理
axios发现响应的
Content-Type是json,就会自动调用JSON.parse,所以正确设置Content-Type是好习惯 -
请求拦截器
可以在所有请求里面加些东西,比如加查询参数
-
响应拦截器
可以在所有响应里加些东西,甚至改内容
-
可以生成不同实例(对象)
不同的实例可以设置不同的配置,用于复杂场景


























![[桜井宁宁]COS和泉纱雾超可爱写真福利集-一一网](https://www.proyy.com/skycj/data/images/2020-12-13/4d3cf227a85d7e79f5d6b4efb6bde3e8.jpg)

![[桜井宁宁] 爆乳奶牛少女cos写真-一一网](https://www.proyy.com/skycj/data/images/2020-12-13/d40483e126fcf567894e89c65eaca655.jpg)