文章目录
如何保证批量请求失败,只弹出一个toast
1使用计数器:
初始化一个计数器,记录失败请求的数量。每当请求失败(完成)时,计数器加一。
在所有请求结束后检查计数器的值,如果等于请求总数,则弹出toast。
2使用标志变量:
设置一个标志变量,默认为false。当第一个请求失败时,将标志变量设置为true。
后续的请求失败后,检查这个标志变量,如果是true则不再进行设置,确保toast只弹出一次。
在所有请求结束后,检查标志变量,如果是true则弹出toast。
所有的请求都通过Promise.all
来[并行处理] ,最后检查结果数组中是否有失败的情况,并且确保只弹出一个toast。
function batchRequest(urls) {
let hasFailed = false;
const promises = urls.map(url => {
return request(url).catch(error => {
if (!hasFailed) {
hasFailed = true;
}
return error; // 返回错误,但不直接处理
});
});
Promise.all(promises).then(results => {
// 检查是否有失败的请求
if (results.some(result => result === 'Failure')) {
if (hasFailed) {
showToast('所有请求均失败');
}
}
});
}
如何减少项目里的if-else
1使用多态
将if else语句中的不同分支封装成不同的类,并定义一个共同的接口或基类,通过调用接口或基类的方法来执行相应的逻辑。这样可以将条件判断转移到对象的创建阶段,减少代码中的条件判断语句。
在 JavaScript 中,可以使用对象和函数来实现多态,从而减少 if-else
语句的使用。以下是一个示例:
// 定义一个基类(抽象类)
class Animal {
makeSound() {
throw new Error("makeSound 方法必须在子类中实现");
}
}
// 定义多个子类,每个子类代表一种动物
class Dog extends Animal {
makeSound() {
return "汪汪汪";
}
}
class Cat extends Animal {
makeSound() {
return "喵喵喵";
}
}
class Bird extends Animal {
makeSound() {
return "叽叽叽";
}
}
// 定义一个函数,接收一个 Animal 类型的对象,并调用其 makeSound 方法
function playSound(animal) {
console.log(animal.makeSound());
}
// 创建不同类型的动物对象
const dog = new Dog();
const cat = new Cat();
const bird = new Bird();
// 调用 playSound 函数,传入不同的动物对象
playSound(dog); // 输出:汪汪汪
playSound(cat); // 输出:喵喵喵
playSound(bird); // 输出:叽叽叽
在这个示例中,我们定义了一个 Animal
基类和多个子类(Dog
、Cat
和 Bird
),每个子类都有一个 makeSound
方法。然后,我们定义了一个 playSound
函数,它接收一个 Animal
类型的对象,并调用其 makeSound
方法。这样,我们就可以通过多态来减少 if-else
语句的使用。
2使用策略模式
将if else语句中的不同分支封装成不同的策略类,每个策略类实现一种特定的逻辑。通过将策略类作为参数传递给一个统一的执行方法,根据不同的情况选择相应的策略类执行逻辑。这样可以将条件判断转移到方法的调用阶段,减少代码中的条件判断语句。
3使用字典映射
将if else语句中的不同分支作为字典的键值对,根据条件选择相应的键,然后通过字典获取对应的值执行相应的逻辑。这样可以将条件判断转移到字典的查找过程,减少代码中的条件判断语句。
4使用状态模式
将if else语句中的不同分支封装成不同的状态类,每个状态类表示一种特定的状态,并定义相应的方法。通过将状态类作为属性保存在一个上下文对象中,根据不同的情况切换状态,并调用相应状态类的方法执行逻辑。这样可以将条件判断转移到状态的切换过程,减少代码中的条件判断语句。
babel-runtime 作用是啥
babel-runtime
的主要作用就是**将在使用 babel
进行代码转换时可能被重用的代码抽取成单独的模块,以避免在每个文件中重复出现相同的代码。**它通过模块导入的方式引入这些功能,从而避免了对全局作用域的修改或污染。
使用 babel-runtime
通常需要配合 babel-plugin-transform-runtime
插件一起使用。babel-plugin-transform-runtime
插件会进行一些处理,例如自动导入 babel-runtime/core-js
,并将全局静态方法、全局内置对象映射到对应的模块;将内联的工具函数移除,改成通过 babel-runtime/helpers
模块进行导入;如果使用了 async/generator
函数,则自动导入 babel-runtime/regenerator
模块等。
这样,在代码中如果需要使用特定的功能,只需从 babel-runtime
相应的模块中导入即可,而不是直接使用全局的对象或函数。
如何实现 PDF 预览和下载
1浏览器内置PDF阅读器
浏览器内置PDF阅读器 是实现PDF文件预览的最直接方式。用户只需在HTML中插入<embed>
或<iframe>
标签,并设定其src
属性为PDF文件的URL即可。
<embed src="path/to/your-document.pdf" type="application/pdf" width="100%" height="600px">
<iframe src="path/to/your-document.pdf" width="100%" height="600px"></iframe>
浏览器会自动调用内置的PDF阅读器插件来显示PDF内容,实现预览效果。这种方法的优点是简单快捷、不依赖外部库,但可能在PDF文件较复杂或需要额外交互时遇到限制
2使用PDF.js库
使用JavaScript库PDF.js 是另外一种流行的方式,它是一个通用的、兼容各主流浏览器的PDF阅读器。PDF.js使用HTML5的Canvas API对PDF文件进行渲染,使其能在不支持插件的情况下工作。
要使用PDF.js,你需要首先引入该库:
<script src="https://mozilla.github.io/pdf.js/build/pdf.js"></script>
随后,你可以通过JavaScript代码加载PDF文件,并在指定的Canvas上渲染出来:
pdfjsLib.getDocument('path/to/your-document.pdf').promise.then(function(pdfDoc) {
pdfDoc.getPage(1).then(function(page) {
var canvas = document.getElementById('pdf-canvas');
var context = canvas.getContext('2d');
var viewport = page.getViewport({scale: 1});
canvas.height = viewport.height;
canvas.width = viewport.width;
var renderContext = {
canvasContext: context,
viewport: viewport
};
page.render(renderContext);
});
});
3后端服务生成图像预览
在一些情况下,前端直接处理PDF文件可能不是最佳选择,尤其是涉及到安全性或者性能时。这时你可以选择后端服务生成图像预览
后端服务可以用第三方库,如Python的PyMuPDF或Java的Apache PDFBox,来处理PDF文件,将其转换成图片并发送到前端
这种方法的优点是前端不需要处理复杂的文件渲染,降低了前端的计算和渲染压力。不过,这也意味着增加了后端的处理负载,并需要处理前后端之间的文件传输。
前端请求中断的方式和原理
1 Axios.CancelToken
axios
对象有一个属性叫CancelToken
,该属性提供了中断已经发出去的请求的方式。具体使用方式有两种:
方式一:执行器模式
const CancelTokenFunc = axios.CancelToken;
let cancel;
// 发送请求
axios.get("https://jsonplaceholder.typicode.com/todos/1", {
cancelToken: new CancelTokenFunc(function executor(c) {
// 将 cancel 函数赋值给外部变量
cancel = c;
}),
}).catch((error) => {console.log(error.message);});
// 取消请求
setTimeout(() => {cancel("Operation canceled by the user.");}, 1000);
先获取一个中断构造函数CancelTokenFunc
,用这个构造函数new
出一个实例赋值给get
请求的参数cancelToken
字段。
在调用CancelTokenFunc
构造函数new
出一个实例的时候,我们传入了一个执行器函数,该执行器会接受一个参数,这个参数就是用来控制中断请求的取消函数,接着我们把该参数函数赋值给外部变量,这样就可以在外部需要的时候执行中断请求的操作。
方式二:令牌模式
// 创建一个 CancelToken 源
const CancelTokenFunc = axios.CancelToken;
const { token, cancel } = CancelTokenFunc.source();
// 发送请求
axios.get("https://jsonplaceholder.typicode.com/todos/1", {
cancelToken: token,
}).catch((error) => {
console.log(error.message);
});
// 取消请求
setTimeout(() => {cancel("Operation canceled by the user.")}, 1000);
用CancelTokenFunc
的source
方法生成一个取消令牌源,并从取消令牌源中解构出token
和cancel
字段,然后在GET
请求中将取消令牌源的token
传递给cancelToken
,接着在外部调用请求令牌源的cancel
方法来取消请求。
2 AbortController
AbortController
是一个Web API,用于控制和管理可中止的异步操作,例如 fetch
请求、DOM
操作。接下来我们看看怎么用AbortController
来中止请求。
// 创建一个 AbortController 信号源
const controller = new AbortController();
const { signal } = controller;
// 发送请求
fetch("https://jsonplaceholder.typicode.com/todos/1", {
signal,
}).catch((error) => {
console.log(error);
});
// 取消请求
setTimeout(() => {
controller.abort("Operation canceled by the user.");
}, 1000);
创建一个AbortController
信号源,在fetch
请求的时候传递一个信号给请求的signal
参数,之后便可以在请求的外部通过调用信号源的abort
方法来取消请求。
令牌中断请求原理
cancelToken
需要携带一个回调属性,在外界执行cancel
方法时触发回调。
给cancelToken
挂载一个Promise
实例的属性,然后将这个Promise
属性的resolved
方法传递给cancel
,当执行cancel
函数的时候,其实就是执行resolve()
,从而改变Promise
实例的状态,我们就能在Promise
实例的then
方法中执行需要的操作。
function fetchData(url, options = {}) {
const { cancelToken } = options;
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open('GET', url);
// 监听请求状态变化,处理请求的常规逻辑
// 其他代码
// 监听取消请求
if (cancelToken) {
// 需要在外界调用cancel请求的时候,调用xhr.abort()方法中止请求
// 并调用reject函数将Promise对象的状态改成rejected
cancelToken.promise.then((msg) => {
xhr.abort();
reject(new Error(msg));
})
}
xhr.send();
});
}
将token
声明为对象,并给token
添加一个promise
属性,该属性是一个Promise
实例,并且将Promise
实例的resolve
方法传递给了cancel
变量,当调用执行cancel()
的时候,就是在执行resolve()
,token
的promise
属性就能触发then
回调函数.
function CancelToken() {}
CancelToken.source = function () {
let cancel;
const token = {
promise: new Promise((resolve) => {cancel = resolve})
};
return {
cancel,
token,
};
};
执行器模式原理
- 在
this
上挂载了promise
属性,该属性是一个Promise对象,同时,为了达到在外部触发该Promise对象的状态变更,我们将其resolve
方法保存给了外部变量resolvePromise
。 - 声明构造函数的时候声明了
executor
入参。 - 在执行器调用的时候传入一个函数作为入参,同时在函数内部执行
resolvePromise()
触发this.promise
状态变更。
function CancelToken(executor) {
let resolvePromise;
this.promise = new Promise((resolve) => { resolvePromise = resolve;});
executor(function c() {
resolvePromise();
})
}