throwing-functions二次封装注意点关于主题 至于标题中throwing-function: 如标题,这样
关于主题
至于标题中throwing-function:
“在js中,可能抛出错误的函数,有没有对应的专业术语来形容这样的函数?”
在JavaScript中,可能抛出错误的函数通常被称为“throwing functions”(抛出函数)。——ChatGPT
如标题,这样文章主题应该就比较明确了
背景
概述
写这篇文章的背景是,作者在个人项目中对一些原生api进行二次封装,然后在业务代码去调用封装好的方法,业务代码中妥善的写了错误处理的逻辑,可是当原生api抛出错误时,我们得到了控制台报错而非进行错误处理的逻辑,如Message提示等。换句话说业务代码中没有感知到二次封装方法的抛出错误。
错误代码示意
如下getMediaStream
函数本质就是对navigator.mediaDevices.getUserMedia
这个原生api进行二次封装,增加一些额外的逻辑
import to from 'await-to-js';
// 获取媒体流
export async function getMediaStream(constraints: MediaStreamConstraints = { video: true, audio: true }) {
if (globalMediaStream) {
return globalMediaStream;
}
const [error, stream] = await to(navigator.mediaDevices.getUserMedia(constraints));
if (error) {
return new Error('获取本地媒体失败, 请检查是否开启了摄像头与麦克风');
}
return (globalMediaStream = stream);
}
Plus:其中to
函数是一个很简单的小工具,可以让异步逻辑嵌套更浅,简易源码如下:
function to(promise) {
return promise
.then(function (data) {
return [null, data];
})
.catch(function (err) {
return [err, undefined];
});
}
我们在业务逻辑中使用getMediaStream
如下:
const getMediaStreamConsumer = async () => {
// ...
const [err, stream] = await to(getMediaStream());
if (err) {
return ElMessage.error(err.message);
}
// ...other logic to use stream
}
分析
问题一、类型错误
首先经过如上封装,getMediaStream
函数的返回值变成了MediaStream | Error
,也就是原本navigator.mediaDevices.getUserMedia
方法的返回值和Error
的联合类型,这样我们在getMediaStreamConsumer
函数中拿到stream
去使用的时候还需要使用类型断言来排除Error
干扰,如(stream as MediaStream).xxxFun()
问题二、to方法中失败的错误捕获
如果原生apinavigator.mediaDevices.getUserMedia
执行报错,那么我们希望getMediaStreamConsumer
中可以从to
方法的返回值中解构得到err
,进而触发ElMessage.error
的错误提示,但是事实是err
的值永远为undefined
。
原因就在于我们二次封装throwing functions时没有保持它原来的返回值类型,无论是Error
类型还是什么类型,getMediaStream
函数永远拥有返回值,自然消费它的函数永远拿不到err
,换句话说getMediaStreamConsumer
里的to
方法的执行过程中,没有错误被抛出。
解决方法
二次封装throwing functions时使用throw
抛出错误来保持原api的特点,而不是return
。
如下
import to from 'await-to-js';
export async function getMediaStream(constraints: MediaStreamConstraints = { video: true, audio: true }) {
if (globalMediaStream) {
return globalMediaStream;
}
const [error, stream] = await to(navigator.mediaDevices.getUserMedia(constraints));
if (error) {
// 抛出错误
throw new Error('获取本地媒体失败, 请检查是否开启了摄像头与麦克风');
}
return (globalMediaStream = stream);
}
转载自:https://juejin.cn/post/7371698970975256603