手写axios

//拦截器
class InterceptorsManage{
  constructor(){
    this.handlers=[];
  }

  use(resolve,reject){
    this.handlers.push({
      resolve,
      reject
    })
  }
}

//取消请求
class CancelToken{
  constructor(exactor){
    //将promise给cancel。防止多次重复cancel
    let resolvePromise;//promise实例的resolve方法
    this.promise=new Promise(resolve=>{
      resolvePromise=resolve;
    })
    this.reason=undefined;
    const cancel=message=>{
      if(this.reason){return}
      this.reason='cancel'+message;
      resolvePromise(this.reason);//改变this.promise为resolve状态
    }
    exactor(cancel)
  }
  throwIfRequested() {
    if(this.reason) {
        throw this.reason
    }
  }
  source(){
    let cancel;//等于上面的cancel,一个函数
    const token=new CancelToken(function exactor(c){
      cancel=c;
    });
    return {
      token,
      cancel
    }
  }
}

class Axios{
  constructor(){
    this.interceptors={
      request:new InterceptorsManage,
      response:new InterceptorsManage
    }
  }

  request(config){
    //拦截器和请求组装队列
    let chain=[this.sendAjax.bind(this),undefined];//成对出现,一个成功一个失败,失败回调暂不处理
    //请求拦截,调用request请求时,请求拦截在前,响应拦截在后
    this.interceptors.request.handlers.forEach(inter=>{
      chain.unshift(inter.resolve,inter.reject)
    })
    this.interceptors.response.handlers.forEach(inter=>{
      chain.push(inter.resolve,inter.reject)
    })

    //执行队列,每次执行一对,并给promise最新的值
    let promise=Promise.resolve(config);
    while(chain.length>0){
      //promise状态是resolved会执行then中第一个回调,1.request.use(成功,失败),2.成功把resolve(config)成功结果给到sendAjax参数执行,3.sendAjax执行成功,把结果response给response.use(成功)参数执行
      promise=promise.then(chain.shift(),chain.shift())
    }
    return promise;
  }
  sendAjax(config){
    return new Promise(resolve=>{
      const {url='',method='get',data={}}=config;
      //发送请求
      const xhr=new XMLHttpRequest();
      xhr.open(method,url,true);
      xhr.onreadystatechange=()=>{
        if (xhr.status >= 200 && xhr.status <= 300 && xhr.readyState === 4) {
          resolve(xhr.responseText)
        } else {
          reject('失败了')
        }
      };
      if(config.cancelToken){
        //promise为resolve状态时,取消请求
        config.cancelToken.promise.then(function onCancel(reason){//onCancel就是上面的resolvePromise
          if(!xhr){return}
          xhr.abort();//取消请求
          reject(reason)
          xhr=null;
        })
      }
      xhr.send(data)
    })
  }
};
//定义get,post等方法,挂到Axios上
const methodsArr=['get','delete','head','options','put','patch','post'];
methodsArr.forEach(met=>{
  Axios.prototype[met]=function(){
    console.log('执行'+met+'方法');
    //处理单个方法,带2个参数(url,config)
    if(['get','delete','head','options'].includes(met)){
      return this.request({
        method:met,
        url:arguments[0],
        ...arguments[1]||{}
      })
    }else{//3个参数(url,data,config),put,post有data
      return this.request({
        method:met,
        url:arguments[0],
        data:arguments[1]||{},
        ...arguments[2]||{}
      })
    }
  }
});
//继承类的方法及属性
function extend(to,from,ctx){
  for(let key in from){
    //继承自身属性,不继承原型链,用hasOwnProperty判断
    if(from.hasOwnProperty(key)){
      if(typeof from[key]==='function'){
        to[key]=from[key].bind(ctx)
      }else{
        to[key]=from[key]
      }
    }
  }
  return to;
};


//引用
function createInstance(){
  let context=new Axios();
  //用实例化的context对象去接替Axios类的的request方法,支持axios({...})方法
  let instance=Axios.prototype.request.bind(context);
  //继承get,post,put等方法
  extend(instance,Axios.prototype,context);
  extend(instance,context)
  return instance;
}
let axios=createInstance();
//加拦截器
axios.interceptors.request.use(function(config){
  //发送之前做什么
  return config;
},function(err){
  //请求出错
  return Promise.reject(error)
});
axios.interceptors.response.use(function(response){
  //响应数据做什么
  return response;
},function(err){
  return Promise.reject(err)
})

//取消请求
// const {token,cancel}=cancelToken.source();
// config.cancelToken=token;
// setTimeout(()=>cancel,500)

你可能感兴趣的:(前端)