成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

Ajax 如何保證請(qǐng)求接口數(shù)據(jù)符合預(yù)期?如何去重?

Darkgel / 1748人閱讀

摘要:將的,和包含全部請(qǐng)求參數(shù)的字符串存入管理器。如滿足條件,則當(dāng)前請(qǐng)求不需要發(fā)起。如果參數(shù)不同,或者是人為操作,則視為兩個(gè)不同請(qǐng)求。此時(shí)取消中的,并將當(dāng)前請(qǐng)求重新注冊(cè)。如果不設(shè)置此項(xiàng),則只會(huì)保留最后一次,前面的請(qǐng)求會(huì)被掉。

先描述兩個(gè)場(chǎng)景:

快速點(diǎn)擊分頁(yè)碼1.2.3.4.5...。假設(shè)網(wǎng)絡(luò)不好或接口速度不佳,此時(shí)可能有多個(gè)pending中請(qǐng)求。而我們無(wú)法控制返回順序。假如用戶最后點(diǎn)擊到分頁(yè)5,而最后一個(gè)返回的接口是第三頁(yè)的。那現(xiàn)在雖然頁(yè)碼為5,但實(shí)際展示的數(shù)據(jù)卻是第三頁(yè)的。

以Vue為例,created中調(diào)用接口A,某watch中也調(diào)用接口A。那在頁(yè)面初始化時(shí),A可能被調(diào)用了兩次,如果兩次結(jié)果一致,那除了浪費(fèi),也不會(huì)造成其他嚴(yán)重問(wèn)題??山Y(jié)果不一致,會(huì)概率復(fù)現(xiàn)場(chǎng)景1中描述的問(wèn)題。

解決辦法其實(shí)很多,比如:

調(diào)用時(shí)加鎖,判斷該接口是否處于pending?

pending狀態(tài)時(shí),禁用操作按鈕;

但這些方法不可避免的會(huì)引入多余狀態(tài)。如果同頁(yè)面出現(xiàn)N個(gè)接口,情況會(huì)更糟糕。如何維護(hù)那么多狀態(tài)呢?

其實(shí)我們可以在攔截器中解決這些問(wèn)題,直接貼代碼。邏輯看注釋:

以下以 axios 為例。

請(qǐng)求管理器
多帶帶封裝管理器,是為了攔截器中的代碼邏輯更清晰,也為擴(kuò)展性。假設(shè)你需要在其他地方獲取所有pending中的請(qǐng)求,并將其全部取消。
注意 cancel() 方法中的 this.pendings[name].source.cancel(),要想此方法有效,我們需要在register請(qǐng)求時(shí),將ajax工具中包含取消請(qǐng)求api的對(duì)象作為 source 存入管理器。詳見過(guò)濾器中代碼。
/**
 * requestManage.js 請(qǐng)求管理器
 */
class RequestManage {
  constructor () {
    if (!RequestManage.instance) {
      // 這個(gè)屬性可以用來(lái)判斷是人為操作,還是機(jī)器。
      this.nerveVelocity = 100
      // 進(jìn)行中的請(qǐng)求
      this.pendings = {}
      RequestManage.instance = this
    }
    return RequestManage.instance
  }

  /**
   * 向管理器中注冊(cè)請(qǐng)求
   * @param {String,Number} name - 請(qǐng)求標(biāo)識(shí)
   * @param {*} [payload] - 負(fù)載信息,用來(lái)保存你期望管理器幫你存儲(chǔ)的內(nèi)容
   */
  register (name, payload = {}) {
    payload.time = new Date() * 1
    this.pendings[name] = payload
  }

  /**
   * 取消請(qǐng)求
   * @param {String,Number} name - 請(qǐng)求標(biāo)識(shí)
   * @param {*} [payload] - 包含負(fù)載信息時(shí),銷毀后會(huì)重新注冊(cè)
   */
  cancel (name, payload) {
    this.pendings[name].source.cancel()
    if (payload) {
      this.register(name, payload)
    }
  }

  /**
   * 在管理器中移除制定請(qǐng)求
   * @param {String,Number} name - 請(qǐng)求標(biāo)識(shí)
   */
  remove (name) {
    delete this.pendings[name]
  }
}

export default new RequestManage()
過(guò)濾器
// request.js

import axios from "axios
import { requestManage } from "utils"

const request = axios.create({
  timeout: 5000,
  headers: {
    "Content-Type": "application/json",
    "X-Requested-With": "XMLHttpRequest"
  }
})

/**------------------------------------------------
 * axiox request 攔截器
 * 整體邏輯:
 * 1. 使用請(qǐng)求地址 url 作為請(qǐng)求標(biāo)識(shí)。
 * 2. 將 axios 的 source,和包含全部請(qǐng)求參數(shù)的字符串存入管理器。(因?yàn)閟ource中包含axios的cancel方法)
 * 3. 請(qǐng)求發(fā)起前,查看管理器中是否已存在一個(gè)請(qǐng)求?如果不存在,那注冊(cè)一個(gè)進(jìn)去。
 * 4. 如果已經(jīng)存在,則對(duì)比參數(shù),及判斷是否為機(jī)器。如滿足條件,則當(dāng)前請(qǐng)求不需要發(fā)起。拋出錯(cuò)誤 currentRequestCancelled。
 * 5. 如果參數(shù)不同,或者是人為操作,則視為兩個(gè)不同請(qǐng)求。此時(shí)取消 pending 中的,并將當(dāng)前請(qǐng)求重新注冊(cè)。
 * 6. 使用 escape 配置,人為控制一些特殊接口不受約束。
 */

request.interceptors.request.use(config => {
  const { data, params, url, escape } = config
  const
    requestTime = new Date() * 1,
    source = axios.CancelToken.source(),
    currentBody = `${JSON.stringify(data)}${JSON.stringify(params)}`,
    pendingRequest = requestManage.pendings[url],
    pendingBody = pendingRequest && pendingRequest.body,
    isRobot = pendingRequest && requestTime - pendingRequest.time < requestManage.nerveVelocity

  if (pendingRequest) {
    if (currentBody === pendingBody && isRobot) {
      return Promise.reject(new Error("currentRequestCancelled"))
    } else if (!escape) {
      requestManage.cancel(url, {
        source: source,
        body: currentBody
      })
    }
  } else {
    requestManage.register(url, {
      source: source,
      body: currentBody
    })
  }

  config.cancelToken = source.token

  return config
}, error => {
  // 請(qǐng)求錯(cuò)誤時(shí)做些事
  return Promise.reject(error)
})


/** ------------------------------------------------------------
 * axios response 攔截器
 * 接口正常返回后,在管理器中把對(duì)應(yīng)請(qǐng)求移除。
 * 對(duì) request 時(shí)拋出的錯(cuò)誤做處理。
 */

request.interceptors.response.use(response => {
  const { url } = response.config
  requestManage.remove(url)

  return response
}, error => {
  if (axios.isCancel(error)) {
    throw new Error("cancelled")
  } else if (error.message === "currentRequestCancelled") {
    throw new Error("currentRequestCancelled")
  } else {
    return Promise.reject(error)
  }
})

export default request
封裝API
// api.js
import request from "@/utils/request"

/**
 * escape: true 會(huì)跳過(guò)所有約束。
 * 通常只有一種場(chǎng)景需要這么做:
 * 頁(yè)面初始化時(shí),相同接口同時(shí)發(fā)起多個(gè)請(qǐng)求,但參數(shù)不一致,且多次返回的結(jié)果都會(huì)被使用。如果不設(shè)置此項(xiàng),則只會(huì)保留最后一次,前面的請(qǐng)求會(huì)被 cancel 掉。
 */
export default function (params) {
  return request({
    url: `api_path`,
    method: "GET",
    params: params,
    // escape: true
  })
}
使用
import api from "api.js"

async function getData () {
  try {
    const req = await api({
      a:1,
      b:2
    })
  } catch (error) {
    console.log(error)
  }
}


getData()
getData()
getData()
getData()

// 多次調(diào)用,控制臺(tái)中只有第一次請(qǐng)求完成,并打印 `currentRequestCancelled`. (因?yàn)檫@幾次請(qǐng)求完全一樣)

// 如果不捕獲錯(cuò)誤,控制臺(tái)將報(bào) cancelled 或 currentRequestCancelled 錯(cuò)誤。 

以上僅以 Axios 為例,方法可以擴(kuò)展到所有請(qǐng)求工具

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/103802.html

相關(guān)文章

  • JavaScript 需要檢查變量類型嗎

    摘要:一返回值調(diào)用外部方法獲取的值需要對(duì)類型做判斷,因?yàn)槲覀儗?duì)方法返回的值是有期望值類型,但是卻不能保證這個(gè)接口返回的值一直是同一個(gè)類型。 19年目標(biāo):消滅英語(yǔ)!我新開了一個(gè)公眾號(hào)記錄一個(gè)程序員學(xué)英語(yǔ)的歷程 有提升英語(yǔ)訴求的小伙伴可以關(guān)注公眾號(hào):csenglish 程序員學(xué)英語(yǔ),每天花10分鐘交作業(yè),跟我一起學(xué)英語(yǔ)吧 javascript作為一門動(dòng)態(tài)類型語(yǔ)言,具有很高的動(dòng)態(tài)靈活性,當(dāng)定義函數(shù)...

    Songlcy 評(píng)論0 收藏0
  • 接口測(cè)試常見問(wèn)題

    摘要:同時(shí)我們也可以使用控件來(lái)實(shí)現(xiàn)小型的接口自動(dòng)化來(lái)提高接口測(cè)試效率。接口測(cè)試的流程首先我們要了解需求,熟悉業(yè)務(wù)場(chǎng)景然后根據(jù)需求文檔,接口文檔以及業(yè)務(wù)場(chǎng)景來(lái)編寫測(cè)試用例。 ...

    edgardeng 評(píng)論0 收藏0
  • 回調(diào)、使用Promise封裝ajax()、Promise入門

    摘要:回調(diào)使用封裝入門回調(diào)是啥看這里回調(diào)是什么方應(yīng)杭知乎是一種特殊的函數(shù),這個(gè)函數(shù)被作為參數(shù)傳給另一個(gè)函數(shù)去調(diào)用。 回調(diào)、使用Promise封裝ajax()、Promise入門 1 回調(diào)是啥 call a functioncall a function back callback 看這里:Callback(回調(diào))是什么?---方應(yīng)杭知乎 callback 是一種特殊的函數(shù),這個(gè)函數(shù)被作為參數(shù)...

    godlong_X 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<