前言
Promise,ES6带给前端开发者们最好的礼物,把广大人民群众从异步+回调地狱中解救了出来,在async/await语法糖还没诞生之前,仿佛神兵天降,如今,await泛滥的今天,却已黯然失色不少。笔者今日看到希沃ENOW大前端的一篇文章当面试官问Promise的时候他想知道什么 当中有提及Promise的3个不足之处。
或许就是这样那样的不足之处,让曾经作为神兵利器的它不如语法糖那般呼风唤雨吧。
优化不足
so?当面试官问你一个这样的问题:Promise有什么不足之处呢?或许可以滔滔不绝,娓娓道来,源远流长,甚至可以长篇大论。但是这时候如果被杀回马枪,反将一军,反问一句,如何解决这些不足之处呢?这时候或许就会望而生畏,不知所以了吧。
笔者静心思索一番,采用了高阶函数进行抽象,可以稍许改善以上的不足之处。
逻辑与Promise分离
首先,我们把Promise和内部的副作用(延时脚本/ajax请求等等)逻辑完全分离,只保留接口(res, rej)部分。
function timeoutLog(res, rej) {
setTimeout(() => {
res('look at me !!!!, i am here');
}, 5000);
}
复制代码
timeoutLog 延迟5秒 返回一个结果,而整个函数/逻辑与promise完全解耦。这种写法非常类似一些node server比如express/egg的中间件,只保留了参数(res, rej)这些接口。
高阶函数
原理很简单,函数返回函数就可以了
const useStatusInPromise = f => <T extends any>():[Promise<T>, {value: 'pending' | 'done' | 'rejected'}] => {
// 前面可以加一些对f的健康检查
// 内部保存的promise状态
const status = {
value: 'pending'
};
const promise = new Promise(f).then(res => {
status.value = 'done';
return res;
}).catch(err => {
status.value = 'rejected';
return err;
}).finally(() => {
Object.freeze(status); // 冻结status,防止被重新改写状态
});
return [promise as Promise<T>, status as any];
}
复制代码
结果
// 这里为止promise并不会立即执行 解决一旦新建就会立即执行的不足
// 如果想中途取消就不需要调用makeStatusPromiseLog
const makeStatusPromiseLog = useStatusInPromise(timeoutLog);
// 执行makeStatusPromiseLog 并拿到promise实例和状态对象;
// 这时候才会执行promise
// 同时返回status 在执行环境中都可以访问到promise的最新状态
// 解决了第3点的不足
// 同时泛型约束返回的promise结果类型
const [promise, statusObj] = makeStatusPromiseLog<string>();
const interval = setInterval(() => {
// 执行环境中可以访问promise的状态
if (statusObj.value === 'done' || statusObj.value === 'rejected') {
clearInterval(interval);
promise.then(res => {
console.log('timeoutLog end, the result is -> ', res) // 之后在处理promise 依然可以拿到结果
}).catch(console.error);
// 测试一下重复更改promise状态对象 会抛出异常 修改无效
statusObj.value = 'pending'
} else {
console.log('继续检查promise的状态', statusObj.value);
}
}, 1000);
复制代码
异常处理
对于第2个不足之处,笔者也没想到什么好的解决方法。对于这个场景只能在promise的内部逻辑中自行try catch
function timeoutLog(res, rej) {
setTimeout(() => {
// 为防止内部抛出的错误不会被反馈到外部
// 没想到好办法 只能手动内部try catch
// try {
// throw new Error('happen error')
// } catch (err) {
// rej(err)
// }
res('look at me! I am here');
}, 5000);
}
复制代码
总结
高阶函数是个非常有用的武器,特别是在函数式编程当中随处可见,把高阶运用到一般业务中也可以解决很多的问题。比如promise的一些不足之处也是可以改善的~
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END