在用eggjs做后台服务端时,需要去调微信的接口获取openid等信息,然后返回一个结果给前端。
一开始是这么写的
// 登录的controller
async index(ctx) {
// 接受前端传来的code等信息
request(`https://api.weixin.qq.com/sns/jscode2session?appid=${appid}&secret=${secret}&js_code=${code}&grant_type=authorization_code`,function (error, response, body) {
if(error) { //请求异常时,返回错误信息
return reject(error);
} else {
const bodyJson = JSON.parse(body);
const { session_key, openid } = bodyJson;
// ... 处理数据,保存用户信息等操作
console.log(bodyJson)
ctx.body = {
code: 0,
msg: '登录成功'
}
}
}
}
在测试的时候发现,接口调用后始终没有数据返回,但是在controller中输出bodyJson也一直有值,代表回调函数确实执行了。
查找资料后,有大佬指出是洋葱圈模型没有理解,才会写出这样的代码(还是没怎么懂)。
我的理解是整个代码顺序执行,执行到request是异步的,未触发回调函数(未等待函数执行完成)就执行到controller的末尾,由于没有 ctx.body,就直接返回响应,所以前端是404,接收不到回调函数的返回。之后触发了回调函数所以输出了bodyJson的值,但此时的ctx.body返回已经没有用了。所以正确的思路是等待request执行完拿到结果后再ctx.body返回结果。
修改后的写法
const requestPromise = new Promise((resolve, reject) => { // 需要封装成 Promise,等待执行完返回请求。回调中无法 ctx.body 返回响应
// 获取 session_key 和 openid,并更新用户
request(`https://api.weixin.qq.com/sns/jscode2session?appid=${appid}&secret=${secret}&js_code=${code}&grant_type=authorization_code`, async function (error, response, body) {
if(error) { //请求异常时,返回错误信息
return reject(error);
} else {
const bodyJson = JSON.parse(body);
const { session_key, openid } = bodyJson;
/**
* 处理,保存数据等操作
*/
return resolve({
userId,
openId: openid,
avatar: userInfo.avatarUrl,
city: userInfo.city,
gender: userInfo.gender,
nickname: userInfo.nickName,
province: userInfo.province,
})
}
});
})
const result = await requestPromise;
// 最后接口返回数据
ctx.body = {
code: 0,
msg: 'ok',
}