背景
在小程序中,我们会经常使用官方提供的API,但是大多数API是异步执行的。
例如,微信获取用户地理位置的API:
wx.getLocation({
type: 'wgs84',
success: (res) => {
var latitude = res.latitude // 经度
var longitude = res.longitude // 纬度
}
})
可以看到,success
事件是以异步回调的形式执行的。当获取用户地理位置成功时,才会执行 success
事件。
大量的异步回调,会形成回调地狱,导致代码难以维护。例如,下面是小程序内登录的一段逻辑:
wx.login({
success: (res) => {
const { code } = res;
wx.getUserInfo({
success: (res) => {
const { userInfo } = res;
wx.request({
method: 'POST',
url : '服务端的登录接口',
data: {
code,
userInfo,
},
success: (res) => {
if (res.statusCode === 200) {
const token = res.data.token;
console.log('登录成功');
}
},
fail: (res) => {
console.log('异常...')
},
})
}
})
}
})
简单的描述一下这段代码的逻辑:
- 调用
wx.login
,获取code
- 调用
wx.getUserInfo
,获取userInfo
- 调用
wx.request
,将code
和userInfo
发送给服务器,获取登录token
在这段代码里面,尽管我们省略了大量的异常处理,仍进行了3次嵌套。当遇到复杂的业务时,嵌套的层级会更深,代码将变得异常难维护。
使用Promise简化代码
我们使用Promise
将wx.login
包一层,这样我们就可以链式调用。避免回调嵌套。
const wx_login = () => {
return new Promise((resolve, reject) => {
wx.login({
success: (res) => {
resolve(res);
},
fail: (err) => {
reject(err);
}
})
});
};
wx_login().then((res) => {
const { code } = res;
})
.catch((err) => {
console.log(err);
});
通用 Promise 化API方法
由于小程序的异步API都是success和fail的形式,所以,我们可以封装一个通用的Promise化得方法
// 原文地址:https://segmentfault.com/a/1190000013150196
// promisify.js
module.exports = (api) => {
return (option, ...params) => {
return new Promise((resolve, reject) => {
api(Object.assign({}, option, { success: resolve, fail: reject }), ...params);
});
}
};
const promisify = require('./promisify');
const wx_login = promisify(wx.login);
wx_login().then((res) => {
const { code } = res;
});
使用promisify改进登录的代码
const promisify = require('../../utils/promisify.js');
const get_userinfo = promisify(wx.getUserInfo);
const get_code = promisify(wx.login);
const wx_request = promisify(wx.request);
const HOST = 'https://api.xx.cn';
const login = () => {
return Promise.all([get_userinfo(), get_code()])
.then((results) => {
const userinfo = results[0].userInfo;
const code = results[1].code;
return wx_request({
url: `${HOST}/oauth`,
method: 'POST',
data: {
userinfo,
code,
},
})
}).then(res => {
return res.data;
});
};
login().then((res) => {
const { token } = res;
})
总结
异步编程的最高境界,就是根本不用关心它是不是异步。
使用Promise处理异步流程,帮助我们简化了代码逻辑,避免了层层嵌套的回调,让代码更容易维护。
但是,面条式代码并不优雅,最好是能将async/await
引入到小程序中。代码的逻辑会变得更加清晰,更容易维护。期待,高手指点。