記一次Promise在api接口合併中的實踐

寫在前面的話

  1. 本篇水文發出來以後,有朋友反饋文筆太差,的確是做者的鍋,碼字水平目前就這麼高,只能委屈你們看這篇辣眼睛的文字了,文筆只能慢慢改善。
  2. 還有朋友反饋看懵的,仔細想一想也是做者的鍋,沒有表達清楚,修改重發。
  3. 澄清一下本文和Promise.all沒有半毛錢關係,若是讓你們誤會,見諒。
  4. 有朋友但願快速瀏覽能有一句總結,不想看代碼。這裏解釋一下,其實總結就包含在下文合併接口的解釋裏,本文要解決的業務相對比較小衆,脫離場景談總結也沒啥意義,不看業務場景和代碼,本文真的沒有任何的價值,主要是記錄開發業務的一個思路。

關於接口合併(不知道有沒有專門的術語,暫且如此稱呼)在這裏解釋一下,本文所指的是頁面初始化加載數據是一個api接口,而加載更多數據的是另外一個api接口,前一個接口確定會調用,第二個接口不必定會被調用(用戶觸發),可是咱們把兩個調用接口封裝起來,公用一個業務邏輯,做者比較懶不想給兩個接口分別寫業務邏輯。javascript

一. 前言

上次做者在我的項目中遇到的post預檢請求bug,水了一篇小文《記一次跨域post請求數據之preflight request》,本文也只是記錄在特定項目中如何抽取業務邏輯,封裝兩個api接口公用一段業務邏輯的思路,對讀者朋友們有所啓發,那就最好不過了,有什麼問題或者錯漏之處歡迎你們提出來分享,做者此文權當拋磚引玉。vue

關於接口合併,做者在項目開發文檔中也有描述,有興趣的能夠去瞅瞅。java

二. 貓眼API接口分析

脫離業務談編碼就是耍流氓。下面簡單介紹一下貓眼的接口,其中咱們發如今貓眼的兩個頁面中可使用接口合併,如今以貓眼正在熱映頁面的兩個api爲例。ios

1 初始化獲取當前熱映電影列表

如下都將以 api_1 指稱 初始化獲取當前熱映電影列表api接口git

1.1說明
信息 說明
功能 初始化獲取電影信息
URL //m.maoyan.com/ajax/movieOnInfoList
格式 JSON
HTTP METHOD GET

1.2 請求參數

參數 類型 必選 說明
token String false 登陸以後的憑證

1.3返回字段

字段 類型 說明
movieList Array 電影列表(默認一次返回10條)
total Number 電影總數目, total >= movieList.length
movieIds Array 全部電影ID,總數同total,後續請求更多電影時必須依賴它們
coming Array 更多電影列表,第一次請求一定是空

1.4 接口示例

//m.maoyan.com/ajax/movieOnInfoList?tokengithub

{
  "coming": [],
  "stid": "576591972453269000",
  "movieIds": [247295, 410629, 1206605, 248906, 341139, 1250341, 1218091, 344869, 1243239, 580298, 907653],
  "movieList": [
    "同下方獲取當前熱映更多電影列表接口返回的coming字段"
  ],
  "stids": [
    {"movieId": 247295, "stid": "576591972453269000_a247295_c0"}
  ],
  "total": 11
}
複製代碼

2 獲取當前熱映更多電影列表

如下都將以 api_2指稱 獲取當前熱映更多電影列表api接口ajax

2.1 說明

信息 說明
功能 獲取hot更多電影列表
URL //m.maoyan.com/ajax/moreComingList
格式 JSON
HTTP METHOD GET

2.2 請求參數

參數 類型 必選 說明 列子
token String false 登陸以後的憑證
movieIds String true 請求的電影ID,依賴初始化接口的接口返回字段movieIds "1214652,1229799,1251606"

2.3 返回字段

字段 類型 說明
coming Array 更多電影列表

2.4 接口示例

//m.maoyan.com/ajax/moreComingList?token=&movieIds=1214652%2C1229799%2C1251606%2C1215114json

{
  "coming": [
    {
      "id": 1214652,
      "comingTitle": "2月22日 週五",
      "globalReleased": true,
      "haspromotionTag": false,
      "img": "http://p0.meituan.net/w.h/movie/979266668d0e94dc83956a70d22b4eaa184105.jpg",
      "nm": "朝花夕誓-於離別之朝束起約定之花",
      "preShow": false,
      "rt": "2019-02-22",
      "sc": "9.2",
      "showInfo": "今天10家影院放映21場",
      "showst": "3",
      "star": "石見舞菜香,入野自由,茅野愛衣",
      "version": "",
      "wish": 76220,
      "wishst": 0,
    },
    ...略
  ]
}
複製代碼

上面兩大坨數據,就是做者整理的api接口文檔,仔細觀察兩個api接口的返回字段,都有一個coming字段,做者最初的靈感也是來自於它們,api_1接口的數據列表放在movieList字段中,咱們下面就將以Promise來處理coming和movieList。axios

有朋友關注api_2接口依賴於api_1接口,貓眼的api就是這麼設計的,api_1接口返回了部分電影列表、所有的電影id和電影總數,api_2接口請求只需傳遞電影id就能夠了。其餘公司設計的api接口請求參數可能就是offset和limit。api

三. 方案

要進行接口合併,無非要解決兩個問題, 判斷接口、處理數據

1 判斷接口

api_2接口請求數據的時候一定須要知道請求的是那些電影的ID,那麼咱們確定要在本地定義一個offset做爲數據的偏移量,做者的項目是vue寫的,就放在了vue的組件實例上了。咱們將offset設爲0,第一次請求時offset一定爲0,咱們就將offset的值做爲判斷接口的依據。

下面直接上代碼

/*** * 業務邏輯部分 * 1. isFirst判斷是否第一次請求 * 2. getInfoListAction(isFirst) 獲得最終的api操做函數 getMovieInfoList * 關於 getInfoListAction請參看下文 @src src\api\index.js ***/
import { getInfoListAction } from '@/api'

const { offset, limit, total } = this
const isFirst = offset === 0
const getMovieInfoList = getInfoListAction(isFirst)
getMovieInfoList(params).then(data => {
    // ....數據處理此處略,詳見下文
})
複製代碼

難道直接用if-esle來硬編碼判斷?做者固然不會這麼糊弄你們了。

/** * @addr src/api/index.js * @ getMovieOnInfoList 初始api的操做函數 * @ getMoreComingList 加載更多數據的操做函數 * @ getInfoListAction經過上文的isFirst做爲參數調用來判斷返回 getMovieOnInfoList仍是 getMoreComingList(也就是上文提到的getMovieInfoList) * 關於 getDataByAction 參看下文 @addr src/util/index.js **/
import request from '@/util/request'
import { getDataByAction } from '@/util'

const getMovieOnInfoList = request('/movieOnInfoList')
const getMoreComingList = request('/moreComingList')
export const getInfoListAction = getDataByAction(getMovieOnInfoList, getMoreComingList)

/*** * @addr src/util/index.js * @getDataByAction 使用函數柯里化,接受兩個操做函數返回一個新函數,在業務邏輯中返回最終的api操做函數 **/
export const getDataByAction = (initAction, nextAction) => (isFirst) => isFirst ? initAction : nextAction

// @addr src/util/request.js
import Axios from 'axios'
let baseURL = process.env.VUE_APP_URL

const defaultConfig = {
  baseURL
}

const STATUS_CODE = 200

const instance = Axios.create(defaultConfig)

const request = (url, method = 'get') => (params) => {
  return instance({
    url,
    method,
    ...params
  }).then(resp => {
    if (resp.status === STATUS_CODE) {
      return resp.data
    }
  })
}
export default request
複製代碼

請忽略做者的request函數的醜陋封裝,沒有作錯誤處理,(逃

2 數據處理

由上文可知,咱們最終的api調用函數調用以後實際上是返回了一個Promise{<resolve>:data}

咱們在vue組件實例上定義了movieList存放數據,movieIds存放第一次返回時movieIds字段的數據,total數據總數。

// 接上文的省略的代碼部分
// 暫時忽略params參數,下文有處理詳解

/** * 1. 在promise.then的函數中,咱們從data數據裏取 movieIds, movieList, coming, total字段 * 2.1 咱們以movieIds判斷是第一次調用api接口(其餘字段也能夠,這裏先偷懶),那麼咱們賦值須要的數據 movieIds,total,直接返回movieList數據. * 2.2 若是2.1沒有執行,那麼確定是加載更多數據的接口api_2,咱們直接返回coming字段 * 3. 從2.一、2.2咱們得到了最後的數據Array,判斷數據的長度,更新offset偏移量和movieList數據 * ps: setImgSize是處理圖片的函數,沒必要理會 **/
getMovieInfoList(params).then(data => {
  const { movieIds, movieList, coming, total } = data
  if (movieIds) {
    this.movieIds = movieIds
    this.total = total
    return movieList
  }
  return coming
}).then(data => {
  if (data.length) {
    this.offset += data.length
    this.movieList.push(...setImgSize(data))
    $state.loaded()
  } else {
    $state.complete()
  }
})
複製代碼

3 參數處理

const { offset, limit, total } = this
const isFirst = offset === 0
if (offset && offset > total) return
const movieIds = this.movieIds
  .slice(offset, offset + limit)
  .join()
const params = { params: { ...this.params, movieIds } }
複製代碼

結尾

水到如今終於要結束了,擠一擠好像也沒啥乾貨,關於接口合併,智者見智,萬一之後改接口爆炸了也說不定,做者只是記錄下當前遇到相似狀況的一種處理方案,或者有更好的方案歡迎你們分享,行文錯漏、改善之處歡迎提出來探討。

Tips: 天天水一篇,生活樂無邊。

相關文章
相關標籤/搜索