[Web geolocation] Web地理定位函数Promise封装

82 阅读2分钟

1 实现目标

基于H5、高德、百度、腾讯的geolocation封装一套Promise函数api geolocation;调用方式:

geolocation().then().catch()

2 函数类型描述

  1. geolocation: IGeolocation
interface IGeolocation {
  (
    options?: IGeolocationOptions,
    geolocator?: GeolocatorType
  ): Promise<IGeolocationResult>;
}
  1. options: IGeolocationOptions | prop | type | 描述 | | --- | --- | --- | | accuracyThreshold | number | 精度阈值,取低于该阈值的定位结果,单位:米 | | maximumAge | number | 定位的缓存时间,单位:毫秒 | | timeout | number | 定位的超时时间,单位:毫秒 |
interface IGeolocationOptions {
  accuracyThreshold?: number;
  maximumAge?: number;
  timeout?: number;
}
  1. geolocator: GeolocatorType,地理定位器类型:H5、GD(高德)、BD(百度)、TX(腾讯)
enum GeolocatorType {
  H5 = 1,
  GD,
  BD,
  TX,
}
  1. IGeolocationResult,定位成功后resolve中返回的result数据类型
interface IGeolocationResult {
  readonly accuracy: number;
  readonly address?: any;
  readonly coords: {
    lng: number;
    lat: number;
  };
  readonly heading?: number | null;
  readonly speed?: number | null;
  readonly timestamp: number;
  readonly geolocatorType?: GeolocatorType | null;
  readonly pois?: any[];
  readonly roads?: any[];
  readonly crosses?: number;
}

3 函数封装

H5、高德、百度、腾讯的geolocation api都是采用回调函数的方式调用的,这里采用Promise的方式对其进行封装,每个api的封装方式都是差不多的,这里就以封装H5的geolocation为例:

const h5Locate: IGeolocation = ({ timeout, maximumAge, accuracyThreshold }) =>
  new Promise((resolve, reject) => {
    const option = {
      enableHighAccuracy: true,
      timeout,
      maximumAge,
    };
    navigator.geolocation.getCurrentPosition(
      (data) => {
        const {
          coords: { accuracy, longitude: lng, latitude: lat, heading, speed },
          timestamp,
        } = data;
        ... ...
        resolve({
          accuracy,
          geolocatorType,
          coords: { lng, lat },
          heading,
          speed,
          timestamp,
        });
      },
      (err) => {
        switch (err.code) {
          case err.PERMISSION_DENIED:
            reject(
              generateErrorMsg(
                GeolocationErrorCode.PERMISSION_DENIED,
                err.message
              )
            );
            break;
          ... ...
          default:
            reject(
              generateErrorMsg(GeolocationErrorCode.UNKNOW_ERROR, err.message)
            );
            break;
        }
      },
      option
    );
  });

4 多种定位函数api集成

基于上述封装实现的H5、高德、百度、腾讯Promise函数api,编写geolocation函数实现同时调用这4种定位api,思路如下: 通过geolocation函数入参geolocator: GeolocatorType的类型,优先采用该geolocator类型的定位器;未传入参geolocator的情况下将默认采用Promise.any()函数返回最先实现的Promise对象结果,从而保证定位的实效性。代码实现如下:

export const geolocation: IGeolocation = (options = {}, geolocator) => {
  switch (geolocator) {
    case GeolocatorType.H5:
      return promiseFirst(
        h5Locate(options),
        Promise.any([gdLocate, bdLocate, txLocate].map((fcn) => fcn(options)))
      );
    case GeolocatorType.GD:
      return promiseFirst(
        gdLocate(options),
        Promise.any([h5Locate, bdLocate, txLocate].map((fcn) => fcn(options)))
      );
    ... ...
    default:
      return Promise.any(
        [h5Locate, gdLocate, bdLocate, txLocate].map((fcn) => fcn(options))
      );
  }
};

其中promiseFirst是一个Promise函数,其resolve的结果会优先返回入参中前面的Promise函数resolve的结果。该函数实现如下:

const promiseFirst = <T>(...promiseList: Promise<T>[]): Promise<T> =>
  new Promise((resolve, reject) =>
    Promise.allSettled(promiseList).then((res) => {
      const fulfilledResult = res.find(({ status }) => status === 'fulfilled');
      if (fulfilledResult) {
        resolve(fulfilledResult.value);
      } else {
        reject(res[0].reason);
      }
    })
  );

5 代码项目地址

wzdong26/wzdong-geolocation-ut8utd(github.com)