1.微信小程序中共享登录状态
通常用户打开进入小程序之后会进入到我们的 index 首页,但是也有情况会是通过朋友分享的链接进入小程序,而分享的链接则大概率非首页。在多个页面调用登录显然是不合理的。
那么登录通常是会放在 app.js 中的 onLaunch
方法,而这也是进入小程序首先会被执行的钩子函数。接着会执行 page 页面的 onLoad
方法。这个过程是同步跟着后面调用的。而与此同时登录是异步请求,也就是说页面的 onLoad 方法被调用时登录并没有完成。
那首先想到的是有没有办法在 app.js 中类似告诉后面的 page,等我这里登录完成后你再载入执行你的 onLoad 方法。这里微信小程序没有这种方法!
这里可以用 promise 解决这个问题,直接上代码:
//app.js
App({
onLaunch() {
this.globalData.loginState = new Promise((resolve, reject) => {
wx.login({
success: (res) => {
// 此处可以加上调用自己项目中服务器端拿回来的用户数据data
resolve(data);
},
fail: (res) => {
reject();
},
});
});
},
globalData: {
loginState: null,
},
});
//index.js
var app = getApp();
Page({
onLoad() {
this.globalData.loginState
.then(() => {
// ... 成功的业务逻辑
})
.catch(() => {
// ... 失败的业务逻辑
});
},
});
简析:
app 中的 onLaunch
首先会被执行,传入的函数会被同步调用,同时登录请求会被同步发起。这时 loginState
就是一个 promise 。我们知道 promise 具有状态性,且一旦状态变化之后不可再变化。 其实 page 中的业务逻辑代码并不关心 loginState 此刻的状态是等待还是被决议(包括成功和失败),因为如果是 pending,那我等你就是了,如果是决议了,同样我进行后续的不同处理。值得注意的是一个 promise 是可以多次调用 then 的,这表示在其它 page 里同样的 onLoad
代码,它依然可以正常工作。不论这个 page 是首次进入的页面,还是用户点击跳转后进入的页面。因为我的主体都是 loginState
这个 promise 。
2. 关于操作权限的统一控制判断
需求:
- 用户分为会员用户和非会员用户。
- 会员用户可以不限次数的进行下载视频的功能
- 非会员用户具有下载视频的体验次数,每次使用则弹窗提示剩余体验次数。
对该功能的校验方法的封装:
async function permissionHandler() {
const { content } = await api();
// 会员用户
if (content.isVip) {
return Promise.resolve();
// 非会员用户
} else {
// 有体验次数
if (content.times) {
return MessageBox.confirm(`剩余体验次数${content.times}`);
// 没有体验次数
} else {
return Promise.reject();
}
}
}
// 调用
permissionHandler()
.then(() => {
// 下载视频
})
.catch(() => {
// 不下载视频
});
此处使用 async 函数,它也是基于 promise。首先 await 一个异步请求,当前用户是否是会员用户,如果是会员则直接返回一个状态是成功的 promise,要知道在 async 函数里,返回值必然会是一个 promise,此处你也可以直接 return 省略 Promise.resolve()
,因为即便不写具体的返回值,函数默认返回 undefined,那么此处经过包装等同于 return Promise.resolve(undefined)
,我的建议是总是显示的写出它,便于理解维护。
接下来另一种情况走到非会员处,如果没有体验次数则直接返回一个失败状态的 promise,原理同上。而如果有体验次数,返回的是 MessageBox.confirm()
, 它是一个等待状态的 promise,既然状态正在等待中,那么我的业务逻辑下不下载视频就还未确定。问题是何时确定,恰恰就在弹窗后的点击确定按钮还是取消按钮。点击确定后 MessageBox.confirm()
则从等待变为成功,随即就会执行我的下载视频的业务逻辑,点击取消则相反。
3. 提交表单
- 提交表单前敏感词校验
function sensitiveWordCheck() {
return (
apiSensitive()
//验证通过没有敏感词
.then(() => {
return Promise.resolve();
})
//有敏感词
.catch(() => {
return Promise.reject();
})
);
}
function submit() {
sensitiveWordCheck()
.then(() => {
// 提交
return apiSubmit();
})
.then(() => {
// 提交成功
})
.catch(() => {
// 提交失败
});
}
submit();
首先 submit()
中我希望是干净的主线逻辑,所以敏感词校验写成了一个方法 sensitiveWordCheck()
,因为它需要包含 api 的调用(敏感词是把内容交给后端判断的)即 apiSensitive(),还需要包含对有敏感词的过滤处理。所以有关敏感词的东西我都不希望出现在 submit 方法中,这会让我的主线变得不清晰。
现在来细看 sensitiveWordCheck()
代码,第一行 return 的是什么,化简一下就是:
function sensitiveWordCheck() {
return apiSensitive().then().catch();
}
要知道 apiSensitive()
是异步请求返回一个 promise,接着调用了 then 之后返回的还是 promise,既然是 promise 那可以继续调用 catch,这时返回的依然是 promise。所以整体 return 的是调用 catch 之后的 promise。要知道我们拿到一个 promise 第一件事情应该想的是他是什么状态,然后进行接下来的分析和处理。先来看 apiSensitive()
的不同状态变化的情况
- pending -> fulfilled
此时是成功态表示没有敏感词,可以继续往下走提交表单,此种情况下面几种代码效果是一样的:
function sensitiveWordCheck() {
return apiSensitive()
.then(() => {
return Promise.resolve();
})
.catch(() => {});
}
// 等同于
function sensitiveWordCheck() {
return apiSensitive()
.then(() => {
return; //这里可以省略return
})
.catch(() => {});
}
// 等同于 (甚至可以直接省略then如果你没有其它需要处理的逻辑)
function sensitiveWordCheck() {
return apiSensitive().catch(() => {});
}
then 的回调里如果没有返回一个 promise, 那么同样它会返回一个默认值 undefined,经过包装就是返回 Promise.resolve(undefined)
,当然如果回调里有报错就另当别论了。
- pending -> rejected
如果是失败态则会走到 catch 中,虽然 then 方法有第二个回到函数表示失败状态的回调,但是我的习惯是总是用 catch 来捕获错误,那么这里要知道一个 promise 被 catch 捕获了,就表示错误被处理掉了。如果下面继续有 then 的话,那它会被执行。除非在 catch 里返回一个失败的 promise。在我们的例子中有显示的写return Promise.reject();
就是在告诉后面这里失败了,即便我这里捕获了这个错误,因为我可能有其他的处理。比如我加一点逻辑:如果有敏感词,弹出一个对话框,询问是否需要把敏感词过滤掉,如果选确定则帮他过滤,下面继续提交表单,否则相反不过滤不提交
<script>111</script>
function sensitiveWordCheck() {
return (
apiSensitive()
//有敏感词
.catch(() => {
return MessageBox.confirm(`是否自动过滤敏感词?`)
.then(() => {
// ...自动过滤敏感词的逻辑
return Promise.resolve()
})
// 此处的catch可以省略如果没有额外的逻辑,相当于把错误继续传递到后面
.catch(() => {
return Promise.reject()
})
;
})
);
}
看到这里可以这么理解catch跟then几乎也是一个道理,catch之后依然是一个promise,那他的状态由什么决定,就是由catch里面的回调返回值来决定,这和then是一样的。