回调函数
- setTimeout()这个调用函数我们也称为回调函数callback
- 普通函数按照代码顺序直接调用,而这个函数,需要等待时间,时间到了才去调用这个函数,因此称为回调函数
- 回调函数就是自己不调用,别人调用的函数
异步和回调的关系
- 异步任务需要在得到结果时通知js来拿结果
- 让js写一个函数地址给浏览器,异步任务完成时浏览器调用该函数地址即可
- 同时把结果作为参数传给改函数,这个函数是写给浏览器调用的,所以叫回调函数
- 异步任务需要用到回调函数来通知结果
- 但回调函数不一定只用在异步任务里
- array.forEach(n=>console.log(n))就是同步回调
如何区分同步异步
- 如果一个函数的返回值处于以下三种内部,那么这个函数就是异步函数
- setTimeout、AJAX(即XMLHttpRequest)、addEventListener
同步和异步
- 同步:前一个任务结束后再执行后一个任务,程序的执行顺序与任务的排列顺序是一致的、同步的。
- 异步:在等待这个任务结束时,同时处理其他的任务
同步任务和异步任务执行过程
- 同步任务:都在主线程上执行,形成一个执行栈
- 异步任务:1. 普通时间,如click、resize等;2. 资源加载,如load、error等;3. 定时器,包括setInterval、setTimeout等
- js执行机制:先执行执行栈中的同步任务,异步任务(回调函数)放入任务队列中,一旦执行栈中的所有同步任务执行完毕,系统就会按次序读取任务队列中的异步任务,于是被读取的异步任务结束等待状态,进入执行栈,开始执行
- 由于主线程不断的重复获得任务、执行任务、再获取任务、再执行,所以这种机制被称事件循环(event loop)
promise的用法
- promise指定回调函数的方式更加灵魂,旧的方式必须在启动异步任务前指定,promise:启动异步任务 - 返回promise对象 - 给promise对象棒的回调函数(甚至可以在底部任务结束后指定/多个)
- promise支持链式回调,可以解决回调地狱问题,回调地狱是回调函数嵌套调用,外部回调函数异步执行的结果是嵌套的回调执行的条件,不便于阅读,不便于异常处理
第一步
- return new Promise((resolve,reject)=>{...})
- 任务成功则调用resolve(result)
- 任务失败则调用reject(error)
- resolve和reject会再去调用成功和失败函数
第二步
- 使用 .then(success,fail)传入成功和失败函数
用promise封装的ajax
ajax('get','/xxx')
.then((response)=>{},(request)=>{})
使用promise调用需要改写ajax的封装源代码
ajax = (method.url,options)=>{
return new Promise((resolve,reject)=>{
const{success,fail} = options//析构赋值,等同于const success =options.success const fail = options.fail
const request = new XMLHttpRequest()
request.open(method,url)
request.onreadystatechange = ()=>{
if(request.readyState === 4){
//成功就调用success 失败iu调用fail
if(request.status < 400){
resolve.call(null,request.response)
}else if(request.status >= 400){
reject.call(null,request,request.status)
}
}
}
request.send()
})
}
我们封装的ajax的缺点
- post无法上传数据,request.send(这里可以上传数据)
- 不能设置请求头(request.setRequestHeader.(key,value)
- 可以花时间把ajax写到完美,或者使用jQuery.ajax,或者使用比jquery更高逼格的axios
axios高级用法
- axios如果发现响应的Content-Type是json,就会自动调用JSON.parse
- 所以说正确设置Content-Type是个好习惯
- 请求拦截器:你可以在所有请求里加些东西,比如加查询参数
- 响应拦截器:你可以在所有响应里加些东西,甚至改内容
- 可以生成不同实例,不同的实例可以设置不同的配置,用于复杂场景