记一次Promise在api接口合并中的妙用

关于接口合并(不知道有没有专门的术语,这里姑且如此描述),在这里解释一下,就是打开新页面第一次请求数据是一个api接口,滚动到底部加载更多数据时是另一个api接口

一. 前言

上次作者在个人项目中遇到的post预检请求bug,水了一篇小文《记一次跨域post请求数据之preflight request》,这次再分享一下个人在api接口合并中如何妙用Promise,尽逮着这只羊使劲薅,毛都快秃了(逃;老规矩,本文只是记录在特定项目api中解决问题的思路,对读者朋友们或有启发那就最好不过了,关于接口合并到底好不好,有什么问题、错漏之处欢迎大家提出来分享,作者此文权当抛砖引玉。

关于接口合并,作者在项目开发文档中也有描述,有兴趣的可以去瞅瞅。

二. 背景介绍

脱离业务谈编码就是耍流氓。下面简单介绍一下猫眼的接口,就以猫眼首页api为例。

猫眼API接口分析

1 初始化获取当前热映电影列表

以下都将以 api_1 指称 初始化获取当前热映电影列表api接口

1.1说明
信息 说明
功能 初始化获取电影信息
URL //m.maoyan.com/ajax/movieOnInfoList
格式 JSON
HTTP METHOD GET
1.2 请求参数
参数 类型 必选 说明
token String false 登录之后的凭证
1.3返回字段
字段 类型 说明
movieList Array 电影列表(默认一次返回10条)
total Number 列表总数目
movieIds Array 所有电影ID,总数同total,后续请求更多电影时必须依赖它们
coming Array 更多电影列表,第一次请求必定是空
1.4 接口示例

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

{
  "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接口

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%2C1215114

{
  "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来大做文章。

三. 方案

啰嗦到现在也没进入正题,可能有读者已经不耐烦了,作者也很急的,但是前戏要备足,大家稍等。

要进行接口合并,无非要解决两个问题,判断接口、处理数据

1 判断接口

api_2接口请求数据的时候必定需要知道请求的是那些电影的ID,那么我们肯定要在本地定义一个offset作为数据的偏移量,作者的项目是vue写的,就放在了vue的组件实例上了。我们将offset设为0,第一次请求时offset必定为0,我们就将offset的值作为判断接口的依据。

下面直接上代码

import { getInfoListAction } from '@/api'

const { offset, limit, total } = this
const isFirst = isFirst = offset === 0
const getMovieInfoList = getInfoListAction(isFirst)
getMovieInfoList(params).then(data => {
    // ....数据处理此处略,详见下文
})
复制代码

通过getInfoListAction 和 isFirst我们得到最终的api操作函数getMovieInfoList,关于getInfoListAction请看下面js代码

难道直接用if-esle来硬编码判断?作者当然不会这么糊弄大家了,请看下面

getInfoListAction是由getDataByAction(curry处理)接受两个api调用函数,返回一个接受Boolean参数判断返回哪个api接口函数的新函数,最终也就是上文使用了isFirst调用返回了最终的api函数getMovieInfoList。

// @addr src/api/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
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.3我们获得了最后的数据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: 每天水一篇,生活乐无边。

猜你喜欢

转载自juejin.im/post/5c80b548518825407b2b64a4