ajax
基础知识
回调函数
把一个函数作为实参传给另一个函数,并在另一个函数里调用。
JS中的异常处理
错误类型
Error 所有错误的父类型
TypeError 类型错误
let b console.log(b.xxx) // TypeError:Cannot read property 'xxx' of undefined
ReferenceError 引用的变量不存在
console.log(a) // ReferenceError:a is not defined
SyntaxError 语法错误
const c = """"
// SyntaxError:Unexpected string
RangeError 不在范围内
function fn() {
fn()
}
fn()
// RangeError:Maximum call stack size exceeded
错误处理
抛异常 throw new Error
throw new Error('errorMessage')
(抛错误又不处理的话不会往下执行的)
捕获错误并处理try catch
// 捕获处理异常
try {
something()
} catch (error) {
console.log(error.message)
}
console.log("我能正常执行了")
Promise
重要参考
阮一峰 es6.ruanyifeng.com/#docs/promi…
掘金的promise面试题
Promise的理解:
Promise是用来异步编程的。
从语法上看,Promise是一个对象,有统一的API,可以对各种异步操作进行处理。
从功能来说,Promise是一个容器,保存了一个(未来才会结束的)异步操作的结果。
Promise构造函数
传入一个立即执行函数executor,函数内部代码是同步执行的
函数接收两个回调resolve和reject,这两个回调是执行异步操作后的回调,把当前Promise的状态修改,并把结果PromiseResult传递出去
Promise.resolve()和Promise.reject()
Promise.resolve('foo')
// 等价于
new Promise(resolve => resolve('foo'))
Promise.resolve()用于吧一个已有对象包装成Promise对象
resolve里传的参数分为几种情况(参考阮一峰)
es6.ruanyifeng.com/#docs/promi…
1.不传/传固定值
返回一个Promise对象 状态为resolved 值为undefined/传的固定值
2.传Promise对象
原封不动的返回该对象
3.传thenable对象
即有then方法的对象 把对象转化为promise对象后马上执行then方法
Promise实例对象的两个属性:状态和值
状态属性:PromiseState
pending
resolved/fulfilled :执行resolve函数后
rejected:执行reject函数、或抛出一个错误时
状态一旦变为resolved或rejected就不能变化了
值(结果)属性:PromiseResult
成功的值 value
失败的值 reason
Promise.prototype.then和catch
Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数(第二个可选)
catch接收rejected状态的回调
Promise的then链式调用
.then会返回一个新的Promise对象 所以可以通过.then一直链式调用
返回的新对象的值取决于then里回调函数的返回值,返回固定值则该新对象状态变为resolved,并把这个返回值传给下一个then
也可以返回一个Promise对象,进行一些异步操作,等这个Promise的对象值返回了再进入下一个then
但是执行了then和catch里的错误回调,
抛出错误的话,该新对象变为rejected,值为抛出的error
const p = Promise.reject(1).then(null,err=>{throw err}) //p rejected 1
const p = Promise.reject(1).then(null,err=>err) //p fulfilled 1
const p = Promise.reject(1).catch(err=>err) //p fulfilled 1
Promise.prototype.finally()(手写)
注意哦 原型方法是相对于实例的方法 Promise.race这种是直接调的 接受一个参数为回调函数,无论该promise实例状态如何都会执行finally的回调,相当于then方法的特例。
Promise.prototype.finally = function(callback){
//this为p实例对象,P返回Promise构造函数
const P = this.constructor
return this.then(res=>P.resolve(callback()).then(res=>res))
.catch(err=>P.resolve(callback()).then(err=>{throw err}))
// 这两个err不是一个err 第二个err是callback的返回值吧ithink
}
Promise.all(手写)
接受参数为一个Promise实例对象数组(如果不是promise实例,会用Promise.resolve()包装成Promise数组),返回为一个新Promise实例对象。
当所有对象都resolved的时候,返回的promise状态变为resolved,值是一个数组,包含所有对象的成功值。
当有一个对象rejected的时候,返回的promise状态变为rejected,值是第一个rejected的对象的失败值。
promiseAll = function(promises){
promises = Array.from(promises)//确保有迭代器
const result = []//存放返回值的结果数组
let index = 0//记录resolve的promise的数量
// 返回一个新的promise对象
return new Promise((resolve,reject)=>{
for(const p of promises){
p.then(res=>{
// 遍历每个promise 成功时加入result数组
result.push(res)
index++
// index到所有promise都resolve了
if(index === promises.length-1){
resolve(result)
}
}).catch(
err=>reject(err)
)
}
})
}
Promise.race(手写)
接受参数为一个Promise实例对象数组。
返回一个新的promise实例,该实例的状态和状态取决于该数组里第一个变为rejected或resolved的对象。
promiseAll = function(promises){
promises = Array.from(promises)//确保有迭代器
const result = []//存放返回值的结果数组
let index = 0//记录resolve的promise的数量
// 返回一个新的promise对象
return new Promise((resolve,reject)=>{
for(const p of promises){
p.then(res=>{
// 遍历每个promise 成功时加入result数组
result.push(res)
index++
// index到所有promise都resolve了
if(index === promises.length-1){
resolve(result)
}
}).catch(
err=>reject(err)
)
}
})
}
Promise.allSettled(手写)
Promise.all()方法只适合所有异步操作都成功的情况,如果有一个操作失败,就会报错。
Promise.allSettled接收一个promise实例对象数组,返回一个新的Promise实例对象。
等数组中所有对象都结束后,返回的对象只会变为resolved,结果为一个数组results,包括所有promise实例对象。
promiseAllSettled = function(promises){
promises = Array.from(promises)//确保迭代器
const result = []
let index = 0
return new Promise((resolve,reject)=>{
for(const p of promises){
p.finally(()=>{
result.push(p)
if(++index === promises.length) resolve(result)
})
}
})
}
Promise.any(手写)
和all相反, 只要有一个变成resolved 就返回resolved和相应成功值 所有都rejected返回rejected且抛出一个AggregateError实例,该实例有一个errors属性,保存着错误结果的数组
promiseAny = function(promises){
promises = Array.from(promises)//确保迭代器
const errors = []
let index = 0
for(const p of promises){
p.then(res=>resolve(res))
.catch(err=>{
errors.push(err)
if(++index === promises.length){
const error = new AggregateError()
error.errors = errors
throw error
}
})
}
}
Promise吃掉错误
Promise不指定错误时的回调的话 浏览器虽然会报错 但是不会影响Promise外部代码的正常运行。
(面试题)Promise控制并发数量
并发:同时进行多个任务
原生静态方法Promise.all Promise.race等 不能控制并发数量
思路:
已知一组请求url 已知生成promise请求的请求函数 已知限制最大并发数limit
1、promises数组存已生成的请求(包括pending和已完成的) queue存当前并发中的请求(且请求成功后执行.then返回index和res的数组)
2、初始时 取urls的前limit个生成请求 通过.then返回index和res的数组 加入queue队列 和 promises数组
3、for of遍历剩下的url数组 用await和queue.race 阻塞获取queue中完成的请求的index 对当前url新建请求 插入queue中对应index位置 加入promises数组
4、最后返回Promises.allsettled(promises)
async await
async 和 await 两种语法结合可以让异步代码像同步代码一样,实质是一个自执行的generator生成器函数,generator和yeild的语法糖 不用一个一个调iterator.next().value
async
async声明异步函数,函数会返回一个promise对象,该对象的结果由函数的返回值决定
async function fn() {
// 1. 返回结果为固定值,会包装为resolved的promise对象
// return 'yk'; // Promise {<resolved>: "yk"}
// return; // Promise {<resolved>: undefined}
// 2. 抛出错误,会返回一个失败的Promise
// throw new Error('出错'); // Promise {<rejected>: Error: 出错
// 3. 返回一个Promise
return new Promise((resolve, reject) => {
// resolve('成功的数据');
reject('失败的数据');
})
}
await
await用来暂停异步函数代码的执行,等待promise解决
await 必须写在async函数中,但是async函数中可以没有await
await 右侧的表达式一般为 promise 对象,如果是其他值的话会包装成promise对象(状态resolved)
await的promise失败了,就会抛出异常,需要通过try-catch捕获处理
await 右侧修饰的表达式是同步执行的 后面的代码是类似.then的异步执行的
axios
axios基于promise的http库
axios的文档 www.axios-http.cn/docs/intro
axios二次封装
access_token / refresh_token无感刷新
思想:
1.配置在响应拦截器 在错误回调中判断acess_token是否过期 用refresh_token再发一次请求
2.为了防止多次刷新的情况出现 (在等待新的access_token返回的时候又发了请求) 添加isRefreshing标识是否正在刷新token 和requests数组 用来存放待重发的请求
具体做法:
1.在响应拦截器中 错误回调中判断返回状态码为401
2.判断isRefreshing标识 为true 返回一个Promise 将获取acess_token后重发请求的回调推入requests数组
3.判断isRefreshing标识为false 修改标识为刷新中 调用请求acess_token接口 将acess_token加入config请求头 重发请求 并依次执行requests中的请求 最后调用finally方法修改isRefreshing为false
let isRefreshing = false // 标记是否正在刷新 token
let requests = [] // 存储待重发请求的数组
instance.interceptors.response.use(response => {
return response
}, error => {
if (!error.response) {
return Promise.reject(error)
}
if (error.response.status === 401 && !error.config.url.includes('/auth/refresh')) {
const { config } = error
if (!isRefreshing) {
isRefreshing = true
return refreshToken().then(res=> {
const { access_token } = res.data
setToken(access_token)
config.headers.Authorization = `Bearer ${access_token}`
// token 刷新后将数组的方法重新执行
requests.forEach((cb) => cb(access_token))
requests = [] // 重新请求完清空
return instance(config)
}).catch(err => {
console.log('抱歉,您的登录状态已失效,请重新登录!')
return Promise.reject(err)
}).finally(() => {
isRefreshing = false
})
} else {
// 返回未执行 resolve 的 Promise
return new Promise(resolve => {
// 用函数形式将 resolve 存入,等待刷新后再执行
requests.push(token => {
config.headers.Authorization = `Bearer ${token}`
resolve(instance(config))
})
})
}
}
return Promise.reject(error)
})
吹牛皮时间 把二次封装aixos + 无感刷新token token写成jwt(但jwt应该是后端实现的 所以jwt应该要再补一补)