简介
微信小程序消息订阅是大部分小程序场景都会用到的功能,作为基础开发时,这一类的API 都应该提前被包装好,作为一个工具类库存在。方便团队成员订阅消息时使用。
在大多数情况下,订阅消息都只是配置项和中介方法不一样。前端只需要负责调用微信的API ,然后通知后端订阅的具体模型就算是达到目的了,后端是通过订阅后发送的请求接口 去发送对应的模板数据。
微信小程序 & TaroJS
对于TaroJS 和 微信小程序 来说本体上代码并不会变化太多,所以理论上通用,如果出现问题的话,大概率是微信小程序 上部分ES API 不支持,进行简单的Polifill 解决80% 的问题。
可以看下它们的API 调用方式:
=> 微信
wx.requestSubscribeMessage({
tmplIds: [''],
success (res) { }
})
复制代码
TaroJS =>
Taro.requestSubscribeMessage({
tmplIds: [''],
success: function (res) { }
})
复制代码
可以看到,两者API 做到了完美还原,除了来源方不一致的情况下,大部分都是一样的,甚至Taro 除了支持回调函数的形式外,函数的返回形式是一个Promise ,这意味着我们可以通过使用Promise Status 回调来进行错误和成功的代码回调。
推荐在TaroJS 使用Promise 来进行回调,部分接口(支付)会在回调函数后,依旧throw error,导致错误埋点监控频繁出现。
如何配置数据源
这里数据源分为两块,一块是当前的模板ID , 另一块是请求参数 ,每一次订阅至少有一个模板ID 和一次接口请求 ,这一类属于固定参数。
推荐创建一个config.js 单独保存这一类的东西。
subScriptionTemplateConfig = {
test: ['templateId']
}
subScriptionRequestConfig = {
test: [{ a: 1, b: 1 }]
}
复制代码
声明入口函数
入口函数onSubscriptionTick 作为一个调用主函数main 需要接口两个数组参数,一个是模板id 列表,另一个是请求参数列表。开始之前,会对参数做一个校验,如果不符合那就throw error 出去,直接走catch status ,如果校验过了,那么就可以进行消息订阅。这这里需要注意的是,为了函数不太过复杂,消息订阅也抽出去了一个方法,专门做用户订阅结果处理
export async function onSubscriptionTick(templateId = [], paramList = []) {
if (!templateId) {
throw new Error("throw error: 微信订阅模板id不存在");
}
if (!paramList) {
throw new Error("throw error: 微信订阅请求参数不存在");
}
if (templateId.length !== paramList.length) {
throw new Error("throw error: 订阅模板的消息和请求参数不一致");
}
const asyncCurrentRequestList = [];
await subscriptionStatus(templateId)
.then((res) => {
res.forEach((currentMessage) => {
asyncCurrentRequestList.push(
putSubscribeNews(paramList[currentMessage?.successIndex])
);
});
if (asyncCurrentRequestList.length > 0) {
promiseAllSettled(asyncCurrentRequestList).then((result) => {
for (let index = 0; index < result.length; index++) {
if (result[index].status === "rejected") {
throw new Error(
"throw error: 消息订阅接口失败,订阅任务调度失败"
);
}
}
return true;
});
} else {
throw new Error("throw error: 接口请求参数不存在");
}
})
.catch((err) => {
throw err;
});
}
复制代码
消息订阅的处理
需要注意的是,微信小程序当有多个订阅消息存在的时候,如果用户取消了其中一个,需要对成功的订阅消息ID 进行过滤,尽可能保证通知给后端接口的准确性。
有A,B,C 三条消息需要订阅,用户选择了B,C 的需求,这个时候就需要代码做一次坚持,看看哪些模板ID 是accept , 非accept 都不是成功的订阅,因此,我们只需要将key 为模板ID 属性的值判断以下是不是accept 就可以了。
function subscriptionStatus(templateIds = []) {
return new Promise((resolve, reject) => {
Taro.requestSubscribeMessage({
tmplIds: templateIds,
}).then(res => {
const successTemplateResult = []
templateIds.forEach((id, index) => {
if (res[id] === 'accept') {
successTemplateResult.push({
successId: id,
successIndex: index,
})
}
})
if (successTemplateResult.length > 0) {
resolve(successTemplateResult)
} else {
reject(new Error('订阅错误:当前订阅没有被允许的消息记录'))
}
}).catch(err => {
reject(err)
})
})
}
复制代码
并发请求,只接口状态
Promise 新增加了一个方法叫做Promise.allSettled ,它的作用是执行多个Promise ,将其结果和状态汇聚成一个回调,不论是成功还是失败。不得不说是一个非常方便的接口,但是考虑到兼容性,所以我们决定手写一个Promise 任务队列来实现。实现代码非常的简单,单纯的做为结果返回。方便后续判断订阅状态。
如果当Promise.allSettled 的结果集状态存在rejected 的时候,就说明有接口请求失败了,那么这个消息其实是一个订阅失败的,虽然用户订阅了前端消息,但是后端永远都不用给他发送的。
export function promiseAllSettled(promises) {
let resultArr = []
let runtimeCount = 0
console.log('当前发送的消息', promises)
return new Promise((resolve) => {
function finish(){
if (runtimeCount == promises.length){
resolve(resultArr)
}
}
promises.forEach((promiseTask) => {
promiseTask.then((data) => {
resultArr.push({
status: 'fulfilled',
value: data,
})
runtimeCount++
finish()
}, (data) => {
resultArr.push({
status: 'rejected',
value: data,
})
runtimeCount++
finish()
})
})
})
}
复制代码
可以简单点的优化
-
subscriptionStatus 方法,其实不需要new Promise ,只需要通过async 做一层包装,将出现错误的地方throw error 抛出,正确结果return 出去就好了,会更加的优雅。
-
finally 在promiseAllSettled 中为什么通过一个callback 实现,经过测试,在某些定制机的一个环境下,finally 可能找不到,因此,选择兼容性更好的then & catch 进行处理。
-
判断rejected 的方法其实也可以拆分成一个专门的函数,如果细心的朋友发现了,这一块代码的圈复杂度会在一个19 左右,虽然能看懂,但其实还可以进一步分方法进行。将onSubscriptionTick 当作一个主函数 来进行。
总结
代码来自于团队业务逻辑开发分享,代码并不是很难,主要是如果你正在开发小程序 ,其实没必要在去写一套方案了,拿来开箱即用,节省下开发的时间效率,快速过滤业务逻辑。
不过在闲暇时,可以来杯咖啡,仔细看下代码哦,Copy 的代码最好还是了解下当前的执行逻辑和一些基本流程,这样能够快速的定位到错误发生的地方。
作者:wangly19 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 |