乐趣区

关于前端:手写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)
退出移动版