- 相关技术背景(背景技术),与本发明最相近似的现有实现方案(现有技术)
1.1 背景技术
移动小程序框架mPaas, 为H5应用提供原生能力的自定义接口JSAPI,利用这些接口使用更多的原生功能,提高H5应用的用户体验。例如获取环境变量接口,初始化小程序应用时,有多条H5 http数据请求需要执行,为了保证H5数据请求的正确执行,需要初始化应用之前调用原生获取环境变量接口,根据监听原生异步回调函数获取环境变量,执行下一步的H5 http数据请求。
1.2 与本发明相关的现有技术一
1.2.1 现有技术一的技术方案
由于H5从原生获取环境变量接口数据,是通过原生回调函数异步回传的,为了保证环境变量获取后才能执行下一步的H5 http数据请求。因此数据请求需要在原生接口的回调函数里去执行。
1.2.2 现有技术一的缺点
当有多条H5数据请求时,每一条H5 数据请求都需要在原生回调函数里去执行,才能保证环境变量的正确性,会造成原生接口的频繁调用,甚者会造成页面卡顿、app闪退等现象。
- 本发明技术方案的详细阐述(发明内容)
2.1 本发明所要解决的技术问题(发明目的)
针对现有技术的不足,本发明提供一种一次同步与多次异步结合调用的一种数据请求方法。
2.2 本发明提供的完整技术方案(发明方案)
由于现在小程序开发框架uniapp缓存存储,存在失效的问题,为了数据安全存储,将用户修改的环境变量数据,存储在app端,在h5页面通过调用接口获取app端的缓存环境变量数据。
小程序调用app接口,app返回信息给小程序,由于app执行业务逻辑的线程与小程序执行调用的的线程不在同一个线程上,这个通讯过程天生异步的,无法做到同步,因此
如果不做到同步,小程序里的每一个请求,都需要有环境变量env值才能获取IP地址,才能进行下一步的数据网络请求服务。因而,因此,H5小程序向服务器发出的每一条数据请求之前,均需都要先调用一次SendGetAppInfo接口以获取IP地址,
这会造成app接口的频繁调用,使得app接口(SendGetAppInfo)被频繁调用,进而容易造成页面卡顿、app闪退等现象页面卡顿,甚至app闪退等现象。
为了避免这种现象发生,在本提案中,在H5小程序端,对app的异步接口SendGetAppInfo进行同步封装(promise),让app返回结果事件,最终与H5小程序调用事件在同一线程上执行,并同步获取环境env值后,再执行数据网络请求服务。同时,将同步获取环境env值存储为全局变量,从而,当H5小程序多次向服务器发出数据请求时,可直接使用存储的全局变量,而无需向app重新请求环境变量env值。
请注意:本提案中的异步是指异步接口SendGetAppInfo为异步,同步则是指H5小程序调用SendGetAppInfo事件与获取环境env值同步。也就是说,通过promise对象将异步接口SendGetAppInfo进行同步封装, 在promise封装对象中,将同步和异步的方法相结合,提高了获取环境env值的准确性。
数据网络请求服务又是一个耗时的操作,如果小程序数据请求继续使用同步获取数据,每一条请求又要等着结果出来,再往下执行,那一个页面多条数据请求就需要很长时间,页面就会出现加载慢,闪跳等效果,尤其我们的首页是视频页,体验会非常差。
于是,小程序从app接口同步获取环境env值后,又改为异步网络数据请求方式[Ydf1] 。
通过同步与异步的结合,完成app与小程序之间的异步通讯变为同步获取数据,到小程序与服务器的异步获取数据。[Ydf2]
解决app与小程序端无法同步通讯的问题,以及保证下一步小程序与服务器的正确IP地址请求。
具体代码实现如下:
定义一个async
函数,先调用原生接口,并在原生获取环境变量接口方法前添加await命令,第一次同步调用后全局存储环境变量值,最后返回H5 http异步数据请求Promise对象。参考代码如下:
import store from '../store/index'
import { refreshTokenRequest } from '../api';
import {getLoginInfoFromApp, saveLoginInfoToApp} from '../utils/loginInfo'
let tinyVersion = '2021080303'; // 全局设置版本号,供获取app信息的接口使用
let gdomain = 'i.supor.com';
store.commit('SET_TINY_VERSION', tinyVersion);
let requests = []
let lockRefreshTokenStatus = false
const commonPath = '/supor-food-oss';
function getEnvAsync[周美娜3] () {
return new Promise((resolve, reject) => {[周美娜4]
my.call('sendGetAppInfo', { // 根据app信息的接口判断当前所处环境
time: Date.now(),
tinyVersion: tinyVersion[周美娜5]
}, res => {
switch (res.server) {
case 0:
gdomain = 'i.supor.com';
store.commit('SET_G_DOMAIN', gdomain);
store.commit('SET_ENV_SAVE', {path: gdomain, desc: '当前环境为苏泊尔正式环境', protocol: 'https://'});
break;
case 1:
gdomain = 'aiot-saas.t.supor.com';
store.commit('SET_G_DOMAIN', gdomain);
store.commit('SET_ENV_SAVE', {path: gdomain, desc: '当前环境为苏泊尔验证环境', protocol: 'https://'});
break;
case 2:
gdomain = 'siot-saas.t.supor.com';
store.commit('SET_G_DOMAIN', gdomain);
store.commit('SET_ENV_SAVE', {path: gdomain, desc: '当前环境为苏泊尔测试环境', protocol: 'https://'});
break;
default:
gdomain = 'i.supor.com';
store.commit('SET_G_DOMAIN', gdomain);
store.commit('SET_ENV_SAVE', {path: gdomain, desc: '当前环境为苏泊尔正式环境', protocol: 'https://'});
break;
// 同步获取token信息
if(res.getInfo && typeof res.getInfo === 'string'){
let getInfo = res.getInfo;
getInfo = JSON.parse(getInfo);
if (typeof getInfo === 'object') {
store.commit('SET_TOKEN', getInfo.token)
store.commit('SET_USER_INFO', getInfo.userInfo)
uni.setStorageSync('userInfo', getInfo.userInfo)[周美娜7]
}
}
// 更新是否支持nfc
store.commit('updateAppInfo',res);
})[周美娜10]
})
}
export default async function uniRequest(param = {}) {[周美娜11]
if(store.state.gDomain && store.state.envSave){
//gDomain 值已经存在,并与缓存值相同,不做处理
} else {
await getEnvAsync();[周美娜12]
let { url, method, data, contentType } = param;
data = {
// 其他公共参数
...data
};
url = `${store.state.envSave.protocol}${store.state.envSave.path}` + commonPath + url;
let pattern = new RegExp('https://')
if(!pattern.test(url)) {
url = `https://${gdomain}` + commonPath + url;
method = method || "GET";
let header = {};
header['content-type'] = contentType || 'application/json';
let token = store.state.user.token
let getInfo
if (!token && param.url!="/bossTUser/mobilelogin") {
await getLoginInfoFromApp().then(res=>{
getInfo = res.getInfo;
if (getInfo && typeof getInfo === 'string') {
getInfo = JSON.parse(getInfo)
}
if (typeof getInfo === 'object' && getInfo) {
token = getInfo.token
store.commit('SET_TOKEN', token)
store.commit('SET_USER_INFO', getInfo.userInfo)
uni.setStorageSync('userInfo', getInfo.userInfo)
}
})
}
if (token) {
header['Authorization'] = token
}
let showSelfMsg = data.showSelfMsg || false // 是否由组件自己决定消息提示
return new Promise((resolve, reject) => {
uni.request({
url,
method,
data,
header,
timeout: 10000,
success: function(res) {
let { statusCode, data } = res;
console.log(url + ' res', res)
data = data || {}
if (statusCode === 200) {
if (data.status === 4005 || data.status === 1010) {
reject(data)
} else {
resolve(data);
}
} else {
reject(data)
}
},[周美娜15]
fail: function(err) {
const {data = {message:'网络错误,请稍后重试。'}} = err;
const {message='网络错误,请稍后重试。'} = data;
if(err.errMsg.indexOf("request:fail")!= -1 && err.errMsg.indexOf("timed out") != -1){
store.commit('SET_NETWORK_TYPE', "none");
reject && reject(data);
return
}
if(data.status === 40102) {
let lockLogin = store.state.user.lockLogin
if (!lockLogin) {
store.commit('SET_LOCK_LOGIN_STATUS', true)
setTimeout(() => {
store.commit('SET_LOCK_LOGIN_STATUS', false)
}, 5000)
// 请求一键登录
store.dispatch('showOneClickLoginPage')
}
reject && reject(data);
} else if (data.status === 40101) {
refreshToken(token, {
resolve,
reject,
param
})
} else {
reject && reject(data);
}
}
});
});
}
2.3 本发明技术方案带来的有益效果
第一次进入应用同步调用原生接口,获取环境变量后进行全局存储,只需要调用一次原生接口,保证下一步的每条H5 http数据请求正确,由于H5 http数据请求异步调用,多条请求时不会阻塞进程,加快执行速度,提升用户的体验感。
- 本发明的技术关键点和欲保护点是什么?
通过一次同步调用原生接口,多次异步调用H5 http数据请求,加快应用执行速度,提升用户的体验感。
[Ydf2]同上批注
[周美娜3]封装了一个H5小程序的调用方法,此处只是声明方法
[周美娜4]返回一个异步操作对象promise,先命名为a
[周美娜5]调用APP接口时传的参数,为时间戳和版本号
[周美娜6]这里全局存储环境变量
[周美娜7]这里全局存储token和用户信息
[周美娜8]res参数为APP接口异步回调函数参数,即返回的数据,业务逻辑需要在此回调函数里面执行,否则拿不到需要的缓存数据。
获取的返回对象res,包含server,token ,userInfo等用户信息,一一进行H5小程序全局,存储在内存中,供其它H5页面,其它业务使用
[周美娜9]异步加载成功后的返回数据,也就是promise对象异步操作成功后,通知调用他的方法。遇到resolve方法,调用者才知道此异步请求完成,对下面的awiat有用处
[周美娜10]SendGetAppInfo是APP封装的事件调用接口,供H5小程序调用,获取APP缓存数据,包括环境变量地址server,token ,userInfo等用户信息
[周美娜11]如果引入此js文件,默认导出的方法,uniRequest静态方法声明
[周美娜12]Await命令是保证异步操作获取结果后才会执行下面的业务代码,而获取的结果就是getEnyAsync方法里的resolve方法返回值,但此处我们没有拿返回值,只是保证执行到resolve里就行了,说明resolve上面的业务逻辑都已经执行过了,也就是以上我说过的全部保存环境变量,token, userinfo等信息
[周美娜13]如果全局环境变量有值不做任何处理,如果为空,调用上面声明过得getEnyAsync方法,调用此方法后,由于该方法返回一个异步请求对象promise , 此前我命名为a,会立即执行内部的(立即执行是由promise对象内部机制而来的),APP事件调用,APP事件调用只能异步返回数据,返回的promise对象也是一个异步操作,也就是调用getEnyAsync方法后只能异步返回数据,但是加上await后就将promise对象(a)的异步返回操作变成同步返回,对象a同时也会对内部的getEnyAsync方法调用处理成同步数据返回
[周美娜15]此js文件导出的uniRequest方法,也是返回一个异步请求对象promise(将它命名为b),当其它页面调用uniRequest方法时,异步操作对象b就会执行内部的http请求,也就是h5向服务器的http数据请求,此请求的url地址,也就是请求的api地址包括环境变量(i.supor.com)等数据,是由异步操作对象a提供