零、回顾
在上节课我们主要实践练习了Promise的三个方法,包括any、all、race。
现在我们对Promise变成已经有了一个大致的了解。
这节课我们尝试将登录模块使用Promise编程方式进行改写。
原来在lib/login.js这个模块中,函数loginWithCallback使用的是回调函数的方式通知消费者登录成功。
接下来我们在本课的源码里面将尝试使用Promise的方式进行改写。
一、loginWithCallback函数使用Promise改写
lib/login.js
function loginWithCallback2(e) {
return new Promise((reslove, reject) => {
......
`1.之前的代码逻辑复制粘贴过来`
});
}
export default loginWithCallback2
function loginWithCallback2(e) {
return new Promise((reslove, reject) => {
`2.添加reslove,reject`
......
const requestLoginApi = (code) => {
//发起网络请求
wx.request({
......
success(res) {
......
},
fail(err) {
......
reject(err);
}
})
}
const onUserLogin = (token) => {
......
// if (cb && typeof cb == 'function') cb(token); 注释cb代码
reslove(token);
}
});
// session_key 已经失效,需要重新执行登录流程
wx.login({
......
fail(err){
reject(err);
}
})
}
export default loginWithCallback2
`完整版代码`
function loginWithCallback2(e) {
return new Promise((reslove, reject) => {
let {
userInfo,
encryptedData,
iv
} = e.detail
console.log('userInfo', userInfo);
const requestLoginApi = (code) => {
//发起网络请求
wx.request({
url: 'http://localhost:3000/user/wexin-login2',
method: 'POST',
header: {
'content-type': 'application/json'
},
data: {
code: code,
userInfo,
encryptedData,
iv
},
success(res) {
console.log('请求成功', res.data)
let token = res.data.data.authorizationToken
wx.setStorageSync('token', token)
onUserLogin(token)
console.log('authorization', token)
},
fail(err) {
console.log('请求异常', err)
reject(err);
}
})
}
const onUserLogin = (token) => {
getApp().globalData.token = token
wx.showToast({
title: '登陆成功了',
})
// startOneRequest(token);
// if (cb && typeof cb == 'function') cb(token);
reslove(token);
}
wx.checkSession({
success() {
//session_key 未过期,并且在本生命周期一直有效
console.log('在登陆中');
let token = wx.getStorageSync('token')
if (token) onUserLogin(token)
},
fail() {
// session_key 已经失效,需要重新执行登录流程
wx.login({
success(res0) {
if (res0.code) {
requestLoginApi(res0.code)
} else {
console.log('登录失败!' + res.errMsg)
}
},
fail(err){
reject(err);
}
})
}
})
});
}
export default loginWithCallback2
index.js
// 3.2 登录之后,调用接口
startOneRequestWithLofinUtilMethod1(e) {
......
`loginWithCallback(e, startOneRequest); 注释之前的代码`
`改成promise的写法`
loginWithCallback2(e).then(token=>{
startOneRequest(token);
});
},
`完整版代码`
// 3.2 登录之后,调用接口
startOneRequestWithLofinUtilMethod1(e) {
const startOneRequest = (token)=>{
wx.request({
url: "http://localhost:3000/user/home",
header:{
'Authorization': `Bearer ${token}`
},
success(res) {
if (res.errMsg === "request:ok") console.log("3.2--res1", res);
},
fail(err) {
if (/^request:fail/i.test(err.errMsg)) console.log("3.2--err1", err);
},
complete(res) {
console.log("3.2--complete1", res);
}
});
}
let token = wx.getStorageSync('token')
if (token) {
startOneRequest(token);
return
}
// loginWithCallback(e, startOneRequest);
loginWithCallback2(e).then(token=>{
startOneRequest(token);
});
},
二、await + async
我们可以对具体的每一个,比如像checkSession对它的调用再进行一个改造。
现在仅仅通过Promise,以及相关的all、race等方法,我们现在所能进行的改造是十分有限的。
因为像登录这个场景,它虽然调用了多个接口,但这些接口的调用它前后是有依赖关系的,
并不能简单的按照race或者any这样的逻辑进行改造。
在项目开发中,在很多情况下,我们使用Promise的场景都是比较杂乱的,都不是经典的,
这个时候,为了适应这种编程上的需要,在2016年,Javascript推出了ES7,
包括了两个与Promise编程相关的关键字,一个是await,一个是async。
await操作符,它用于等待一个Promise对象,它只能在异步函数async function中使用,
在function前面必须使用async这样的一种关键字去修饰它。
在上面的代码中,func1它是一个返回Promise对象,它是一个异步操作。
f1是一个标识为async的函数,在它内部使用一个await关键字,去等待一个Promise对象。
await它会暂停当前的async funciton的执行,等待Promise函数处理完成。
如果Promise正常进入了接受状态,其回调的reslove函数,它的参数将作为await表达式的值返回,
然后继续执行下面的代码。
如果Promise它进入了拒绝状态,await表达式会把Promise函数的异常给抛出来,
当前的函数会中止执行,
在上面的代码中可以看出来,OK是不会打印出来的,
var z = await Promise.reject("error");这一行它会中止执行,
因为这句话它抛出了一个异常一个错误,然后这个代码在抛出异常以后,
它通过try catch 它就进入下面catch,它就打印error。
Promise.reject它代表的是创建一个有拒绝状态的Promise对象。
不是一个Promise对象
如果等待的它不是一个Promise对象,则返回这个值得本身。
20它不是一个Promise对象。
await关键字,它还可以用于同步操作
,这可以保证我们代码风格的一致性
三、用await + async改写登录模块代码和调用代码
login.js
wx.checkSession({
success() {
//session_key 未过期,并且在本生命周期一直有效
console.log('在登陆中');
let token = wx.getStorageSync('token')
if (token) onUserLogin(token)
`还差一块逻辑,就是token不存在,也要进行wx.login,
所以checkSession 无论走到哪个,都要进行wx.login`
},
fail() {
wx.login({
success(res0) {
if (res0.code) {
requestLoginApi(res0.code)
} else {
console.log('登录失败!' + res.errMsg)
}
},
fail(err) {
reject(err);
}
})
}
})
function loginWithCallback3(e) {
return new Promise(async (reslove, reject) => {
let {
userInfo,
encryptedData,
iv
} = e.detail
const app = getApp();
try {
app.wxp.checkSession();
} catch (err) {
reject(err);
}
let token = wx.getStorageSync('token')
if (!token) {
let res1 = await wx.login().catch(err => reject(err));
let code = res1.code;
let res = await app.wxp.request({
url: 'http://localhost:3000/user/wexin-login2',
method: 'POST',
header: {
'content-type': 'application/json'
},
data: {
code: code,
userInfo,
encryptedData,
iv
}
}).catch(err => reject(err));
token = res.data.data.authorizationToken;
wx.setStorageSync('token', token);
onUserLogin(token);
const onUserLogin = (token) => {
getApp().globalData.token = token
wx.showToast({
title: '登陆成功了',
})
reslove(token);
}
}
});
}
export default loginWithCallback3
index.js
// 3.2 登录之后,调用接口
startOneRequestWithLofinUtilMethod1(e) {
......
// loginWithCallback(e, startOneRequest);
loginWithCallback3(e).then(token=>{
startOneRequest(token);
});
},
`完整版代码`
startOneRequestWithLofinUtilMethod1(e) {
const startOneRequest = (token)=>{
wx.request({
url: "http://localhost:3000/user/home",
header:{
'Authorization': `Bearer ${token}`
},
success(res) {
if (res.errMsg === "request:ok") console.log("3.2--res1", res);
},
fail(err) {
if (/^request:fail/i.test(err.errMsg)) console.log("3.2--err1", err);
},
complete(res) {
console.log("3.2--complete1", res);
}
});
}
let token = wx.getStorageSync('token')
if (token) {
startOneRequest(token);
return
}
// loginWithCallback(e, startOneRequest);
loginWithCallback3(e).then(token=>{
startOneRequest(token);
});
},
运行
四、catch的简化
目前,我们的wx.request方法还有一个问题,
就是在网络请求发生错的时候,它每个请求都需要你去catch一下,
其实这个代码还可以进一步的简化。
因为只有成功,进入接受状态的时候,然后才可以进入下一步代码的执行,
如果说不能成功进入的话,上面的话,无论我们catch或者不catch的话,其实它这个结果都是一样的。
接下来我们在本课的源码中,对wxp对象进行进一步的一个改造,
改造以后,相关的catch代码就不需要了。
同时将token自动放在网络请求的header中,
如果这个token存在的话,毕竟在每个请求中,手动放入这个token并不是一个好办法。
那么我们一起来看一看,具体的代码怎么去写。
app.js
//app.js
// 初始化只需要初始化一次,只需要在app.js初始化一次就可以了,这样每个页面都可以应用wxp对象
import { promisifyAll } from 'miniprogram-api-promise';
const wxp = {}
promisifyAll(wx, wxp)
wxp.request2 = function (args) {
let token = wx.getStorageSync('token');
if (token) {
if (!args.header) args.header = {}
args.header["Authorization"] = `Bearer ${token}`
}
return wxp.request(args).catch(err => console.log("err", err))
}
App({
wxp: wxp,
......
})
login.js
function loginWithCallback3(e) {
return new Promise(async (reslove, reject) => {
let {
userInfo,
encryptedData,
iv
} = e.detail
const app = getApp();
try {
app.wxp.checkSession();
} catch (err) {
reject(err);
}
let token = wx.getStorageSync('token')
if (!token) {
let res1 = await wx.login().catch(err => reject(err));
let code = res1.code;
let res = await app.wxp.request2({
url: 'http://localhost:3000/user/wexin-login2',
method: 'POST',
header: {
'content-type': 'application/json'
},
data: {
code: code,
userInfo,
encryptedData,
iv
}
});
token = res.data.data.authorizationToken;
wx.setStorageSync('token', token);
onUserLogin(token);
const onUserLogin = (token) => {
getApp().globalData.token = token
wx.showToast({
title: '登陆成功了',
})
reslove(token);
}
}
});
}
export default loginWithCallback3
index.js
// 3.2 登录之后,调用接口
async startOneRequestWithLofinUtilMethod1(e) {
// const startOneRequest = (token)=>{
// wx.request({
// url: "http://localhost:3000/user/home",
// header:{
// 'Authorization': `Bearer ${token}`
// },
// success(res) {
// if (res.errMsg === "request:ok") console.log("3.2--res1", res);
// },
// fail(err) {
// if (/^request:fail/i.test(err.errMsg)) console.log("3.2--err1", err);
// },
// complete(res) {
// console.log("3.2--complete1", res);
// }
// });
// }
// let token = wx.getStorageSync('token')
// if (!token) {
// // startOneRequest(token);
// startOneRequest();
// return
// }
// loginWithCallback(e, startOneRequest);
// loginWithCallback3(e).then(token => {
// // startOneRequest(token);
// startOneRequest();
// });
if (!wx.getStorageSync('token')) {
await loginWithCallback3(e);
}
let res1 = await getApp().wxp.request2({
url: "http://localhost:3000/user/home",
});
console.log("res1",res)
},
运行
五、总结
但是目前还有一个问题,我们目前的网络请求,都必须通过一个open-type,等于getUserInfo的button发起,
否则我们的接口请求可能就会失败,我们不能再javascript代码中发起一个网络请求吗?
如果我们在javascript代码中发起网络请求的时候,如果发现用户未登录,
能否先引导用户去登录,然后再继续接口的调用。答案肯定是可以的。
事实上很多小程序也是这么实现的。默认情况下,一些小程序的浏览时不需要登录的,
只有进行写入操作的时候才要求用户进行登录。这个问题就留给自己思考一下。
这节课我们主要基于Promise加await、async关键字改写了登录模块的代码,
但是我们在自动登录这一块,还仍有问题,
下节课我们看一下,如何在接口调用中实现自动登录