JavaScript 系列之:Ajax、Promise、Axios
前言
-
同步:会阻塞。同步代码按照编写的顺序逐行依次执行,只有当前的任务完成后,才会执行下一个任务。
-
异步:异步代码不会阻塞后续代码的执行。当遇到异步操作时,JavaScript 会将该操作放入任务队列中,继续执行后续的同步代码,直到同步代码执行完毕,再从任务队列中取出异步操作的结果进行处理。
Ajax、Promise、Axios 他们都是异步相关的技术。
-
Ajax 用来发起网络请求;
-
Promise 是 JavaScript 的一种异步编程解决方案(不仅限于异步网络请求),可以使用 Promise 来封装 Ajax 网络请求;
-
Axios 就是使用 Promise 来封装 Ajax 请求的。
Ajax
Ajax(Asynchronous Javascript And XML),翻译为异步的 Javascript 和 XML,它不是编程语言,是一项 Web 应用程序技术。能够在不重新加载整个页面的情况下更新部分网页内容。
Ajax 通过 XMLHttpRequest 对象来向服务器发出异步请求,从服务器获得数据,然后用 JavaScript 来操作 DOM 从而更新局部页面。
特点:
-
异步通信
-
发送请求后,程序并不会等待响应结果,而是会继续往下运行
-
所以,必须要在 Ajax 状态监听的回调函数中,才能保证获取响应数据
-
-
刷新数据而不会加载整个页面
-
不用 Ajax:更新或提交内容——需要重新加载整个网页
-
使用 Ajax:更新或提交内容——只更新部分网页
-
-
无需插件
- 使用纯粹的 JavaScript 和浏览器内置的 XmlHttpRequest 对象
缺点:
-
Ajax 不能使用 back 和 history 功能,即对浏览器机制的破坏。
-
安全问题:Ajax 暴露了与服务器交互的细节
XMLHttpRequest 对象
XMLHttpRequest 对象是由浏览器提供的,它是浏览器的内置对象,而不是 JavaScript 的内置对象。可以通过 window.XMLHttpRequest 得到。
常用的方法:
-
open(get/post, url, 是否异步默认true):创建 http 请求
-
send():发送请求给服务器
-
setRequestHeader():设置头信息(使用 post 才会用到,get 并不需要调用该方法)
-
onreadystatechange:用于监听 ajax 的工作状态(readyState 变化时会调用此方法)
常用的属性:
-
readyState:用来存放 XMLHttpRequest 的状态,监听从0-4发生不同的变化
-
0: 还未创建请求,即未调用 open() 方法
-
1: 已调用 open() 方法,但未发送 send() 方法
-
2: 已调用 send() 方法,但未接收到响应
-
3: 已接收到部分响应
-
4: 已接收到全部的响应
-
-
status:服务器返回的状态码
-
responseText:服务器返回的文本内容
Ajax 如何解决浏览器缓存问题
-
在 Ajax 发送请求前加上
anyAjaxObj.setRequestHeader("If-Modified-Since", "0") -
在 Ajax 发送请求前加上
anyAjaxObj.setRequestHeader("Cache-Control", "no-cache") -
在 URL 后面加上一个随机数:
"fresh=" + Math.random() -
在 URL 后面加上时间戳:
"nowtime=" + new Date().getTime()
手写 Ajax
Ajax 的基本流程:创建 XMLHttpRequest 对象 => 发送数据 => 接收数据
发送 get 请求代码示例:
// 创建一个新的 XMLHttpRequest 对象
var xhr = new XMLHttpRequest();// 配置 GET 请求,URL 后面加上参数
xhr.open('GET', 'https://example.com/api?name=John&age=30', true);// 设置请求头 (可选)
xhr.setRequestHeader('Content-Type', 'application/json');// 监听请求完成后的回调
xhr.onreadystatechange = function() {if (xhr.readyState === 4 && xhr.status === 200) {console.log('GET 请求成功:', xhr.responseText);} else {console.log('GET 请求失败:', xhr.status);}
};// 发送请求
xhr.send();
发送 post 请求代码示例:
// 创建一个新的 XMLHttpRequest 对象
var xhr = new XMLHttpRequest();// 配置 POST 请求
xhr.open('POST', 'https://example.com/api', true);// 设置请求头
xhr.setRequestHeader('Content-Type', 'application/json');// 请求完成后的回调
xhr.onreadystatechange = function() {if (xhr.readyState === 4 && xhr.status === 200) {console.log('POST 请求成功:', xhr.responseText);} else {console.log('POST 请求失败:', xhr.status);}
};// 准备 POST 请求数据
var data = {name: 'John',age: 30
};// 发送 POST 请求,并传递数据
xhr.send(JSON.stringify(data));
Promise
ES6 新特性,Promise 是 JavaScript 中用于处理异步操作的一种方案。本质是一个构造函数,参数是一个执行器函数。
// 创建实例
var promise = new Promise((resolve, reject)=> {console.log(1)// 异步操作setTimeout(() => {const success = true; // 模拟异步操作成功if (success) {console.log(2)resolve('操作成功');} else {reject('操作失败');}}, 2000);
}).then(res =>{// 成功时的回调函数,即 resolve 被调用时执行。console.log(3, res) // 3 操作成功
}).catch(err =>{// 失败时的回调函数,即 reject 被调用时执行。
});// 立即输出 1,等待两秒钟后输出 2,再立即输出 3
初学者可能不太理解这段代码,我们将它拆分开:
// 定义一个执行异步操作的函数
function executorFunction(successMethod, errorMethod) { console.log(1); // 异步操作前的日志 // 使用 setTimeout 模拟异步操作 setTimeout(() => { const success = true; // 模拟异步操作成功 if (success) { console.log(2); // 异步操作成功时的日志 successMethod('操作成功'); // 调用 successMethod,表示操作成功 } else { errorMethod('操作失败'); // 调用 errorMethod,表示操作失败 } }, 2000); // 模拟异步操作耗时 2 秒
} // 定义成功时的回调函数
function onSuccess(res) { console.log(3); // 异步操作成功后的日志 console.log(res); // 打印 successMethod 传递的值
} // 定义失败时的回调函数
function onError(err) { console.log('发生错误:', err); // 打印 errorMethod 传递的错误信息
} // 使用构造函数创建 Promise 对象,构造函数接受一个执行器函数作为参数
var promise = new Promise(executorFunction); // 使用 .then() 和 .catch() 链接 Promise
promise.then(onSuccess).catch(onError);
在上面的示例中,function executorFunction(successMethod, errorMethod) 就是执行器函数,执行器函数在创建 Promise 实例时立即被调用,此时会将 PromiseState(即 Promise 的状态值) 初始化为 pending。
执行器函数接受两个回调函数作为参数,调用第一个参数时表示成功,会将 PromiseState 改为 fulfilled,并将成功的结果传递给后续的 then 方法;调用二个参数时表示失败,会将 PromiseState 改为 rejected,并将错误的结果传递给后续的 catch 方法。
什么是执行器函数?
执行器函数就是那些被设计来执行特定任务或操作的函数,它与普通函数并没有什么区别,只是在叫法上更加细致。
为什么要有执行器函数?为什么不直接使用 resolve 和 reject 作为参数?
其实就是为了封装和控制。其实执行器函数内部封装了很多隐藏的方法。例如 Promise 的状态转换,是怎么从 pending 转为 fulfilled 或 rejected 的呢?还有调用 resolve('操作成功') 方法时,Promise 链是怎么拿到结果的呢?等等这些都是执行器函数在发挥作用。没有执行器函数是无法做到这些的。
为什么要在 .then() 里操作成功时的回调函数,而不是直接在 resolve 方法之前操作?
这是一个强烈的建议,但并不是一个唯一标准。
-
异步性:Promise 的主要目的是处理异步操作。如果你在 resolve 之前直接进行操作,那么这些操作就会是同步的,而不是异步的。
-
链式调用:Promise 支持链式调用,允许你通过
.then()方法将多个异步操作连接在一起,如果你在 resolve 之前直接进行操作,这种链式调用就无法实现。 -
错误处理:Promise 通过
.catch()方法提供了集中的错误处理机制。如果你在 resolve 之前直接进行操作,并且这些操作可能抛出错误,那么你就需要使用传统的 try-catch 语句来捕获这些错误。 -
解耦:将操作放在
.then()中可以将这些操作与 Promise 的创建和解析解耦。
Promise 特点
-
对象的状态不受外界影响
-
这个好理解,每一个 Promise 对象都是通过构造函数 new 出来的,状态只在该构造函数内部有效
-
有三种状态:pending(等待状态)、fulfilled(成功/已解决状态)、rejected(失败/被拒绝状态)
-
-
有 PromiseState 属性和 PromiseResult 属性
-
PromiseState 值是当前状态值(pending、fulfilled、rejected 中的一种),
-
PromiseResult 值是当前结果值(resolve 或 reject 中的值)。
-
-
一旦状态改变,就不会再变
let p = new Promise((rosolve, reject)=> {rosolve('成功') // 第一次状态改变,一旦状态被改变,后面的代码就都不执行了reject('失败') // 再改变无效consoloe.log('我不会被执行') }) console.log(p)

Promise 的 .then() 和 .catch()
-
.then()接收 1-2 个回调函数作为参数:-
第一个参数是当 Promise 成功(fulfilled)时调用的函数。
-
第二个参数可选,是当 Promise 失败(rejected)时调用的函数。
-
-
.catch()接收 1 个回调函数作为参数-
参数是当 Promise 失败(rejected)时调用的函数。
-
如果
.then()定义了第二个参数,那么当 Promise 被拒绝时会调用 then 的第二个参数函数,而不是 catch 中的参数函数
-
let p1 = new Promise((rosolve, reject) => {setTimeout(() => {throw new Error('出错了'); })
})
.then(data => {console.log('then data:', data);
})
.catch(error => {console.error('catch error:', error);
});
// 没有调用 rosolve 或 reject,then 和 catch 不执行,哪怕是抛出异常也不会执行。
// .then() 接收一个参数
let p1 = new Promise((rosolve, reject) => {setTimeout(() => {rosolve('成功') // rosolve})
})
.then(data => {console.log('then data:', data); // then data: 成功
})
.catch(error => {console.error('catch error:', error);
});let p2 = new Promise((rosolve, reject) => {setTimeout(() => {reject('失败') // reject})
})
.then(data => {console.log('then data:', data);
})
.catch(error => {console.error('catch error:', error); // catch error: 失败
});
// .then() 接收两个参数
let p = new Promise((rosolve, reject) => {setTimeout(() => {reject('失败') // reject})
})
.then(data => {console.log('then data:', data);},error => {console.error('then error:', error); // then error: 失败}
)
.catch(error => {console.error('catch error:', error);
});
既然 then 方法的第二个参数也可以用于处理 reject 的情况,那为什么还要有 catch 方法呢?为什么要有两个重复的功能?
从下面要介绍的 Promise 链式调用中你会找到答案。
Promise 链式调用
链式调用的原理
-
then 方法接收两个可选的回调函数作为参数,这两个参数是有返回值的,它们的返回值被用作下一个 then 方法的参数(如果不定义返回值就是 return undefined)
-
then 方法的返回值其实是一个 Promise 对象,如果 then 中 return 的是一个非 Promise 类型的值(如字符串、undefined 等),就会自动将这个值包裹成一个 PromiseStatus 为 fulfilled、PromiseResult 为这个值的 Promise 对象。例:return 1 会变成 return Promise.resolve(1)。
// then 方法的返回值其实是一个 Promise 实例
const p = new Promise((rosolve, reject) => {setTimeout(() => {rosolve('成功') // rosolve}, 2000)
})const p1 = p.then(data => {console.log('then 1 data:', data);return 1},error => {console.error('then 1 error:', error);}
)console.log('p1:', p1);/*
then 1 data: 成功
[[PromiseState]]: "fulfilled"
[[PromiseResult]]: 1
*/
// 返回值被用作下一个 then 方法的参数
const p = new Promise((rosolve, reject) => {setTimeout(() => {rosolve('成功') // rosolve}, 1000)
})p.then(data => {console.log('then 1 data:', data); // then 1 data: 成功return 1},error => {console.error('then 1 error:', error);}
)
.then(data => {console.log('then 2 data:', data); // then 2 data: 1
});
链式调用的规则
1、如果在上一个 then 或 catch 中捕获了 reject,那么最终都会被下一个 then 的成功回调函数捕获:
// 情况 1:then 的失败回调捕获 reject
const p = new Promise((rosolve, reject) => {setTimeout(() => {reject('失败') // reject}, 1000)
})p.then(data => {console.log('then 1 data:', data);return 1},error => {// then 的失败回调捕获 rejectconsole.error('then 1 error:', error); // then 1 error: 失败return 2}
)
.then(data => {console.log('then 2 data:', data); // then 2 data: 2},error => {console.error('then 2 error:', error);}
);
// 情况 2:catch 捕获 reject
const p = new Promise((rosolve, reject) => {setTimeout(() => {reject('失败') // reject}, 1000)
})p.then(data => {console.log('then 1 data:', data);return 1},
)
.catch(error=> {// catch 捕获 rejectconsole.error('error 1 error:', error); // error 1 error: 失败return 2
})
.then(data => {console.log('then 2 data:', data); // then 2 data: 2},error => {console.error('then 2 error:', error);}
);
2、如果在上一个 then 或 catch 中没有捕获 reject,或 return 了一个 reject,或抛出了异常,那么最终会被下一个 then 的失败回调函数或下一个 catch 捕获:
// 情况 1:没有捕获 reject
const p = new Promise((rosolve, reject) => {setTimeout(() => {reject('失败') // reject}, 1000)
})p.then(data => {console.log('then 1 data:', data);return 1},// error 中没有捕获 reject,代码屏蔽了//error => {// console.error('then 1 error:', error);// return 2//}
)
// catch 也没有捕获 reject,代码屏蔽了
//.catch(error=> {
// console.error('error 1 error:', error);
// return 2
//})
.then(data => {console.log('then 2 data:', data);},// 会被第二个 then 的 失败回调捕获error => {console.error('then 2 error:', error); // then 2 error: 失败}
);
// 或者被第二个 catch 捕获
//.catch(error => {
// console.error('catch 2 error:', error); // catch 2 error: 失败
//})
// 情况 2:return 了 reject
const p = new Promise((rosolve, reject) => {setTimeout(() => {rosolve('成功') // rosolve//reject('失败') // reject}, 1000)
})p.then(data => {console.log('then 1 data:', data); // then 1 data: 成功return new Promise((rosolve, reject)=> {setTimeout(()=> {reject('失败 2') // reject})})},// 无论是在成功回调还是失败回调中使用 return reject//error => {// console.error('then 1 error:', error); // then 1 error: 失败// return new Promise((rosolve, reject)=> {// setTimeout(()=> {// reject('失败 2') // reject// })// })//}
)
.then(data => {console.log('then 2 data:', data);},// 会被第二个 then 的 失败回调捕获error => {console.error('then 2 error:', error); // then 2 error: 失败 2}
)
// 或者被 catch 捕获
//.catch(error => {
// console.error('catch 2 error:', error); // catch 2 error: 失败 2
//})
// 情况 3:抛出异常
const p = new Promise((rosolve, reject) => {setTimeout(() => {rosolve('成功') // rosolve}, 1000)
})p.then(data => {console.log('then 1 data:', data); // then 1 data: 成功throw new Error('抛出异常')}
)
.then(data => {console.log('then 2 data:', data);},// 会被第二个 then 的 失败回调捕获error => {console.error('then 2 error:', error); // then 2 error: 抛出异常}
)
// 或者被 catch 捕获
//.catch(error => {
// console.error('catch 2 error:', error); // catch 2 error: 抛出异常
//})
链式调用的作用
1、可以按顺序执行多个异步操作。例如先获取用户信息,再根据用户信息获取部门 ID,再根据部门 ID 获取部门信息…
const p = new Promise((resolve, reject) => { // 获取用户信息 setTimeout(() => { const userInfo = { name: '张三', userId: '1' }; resolve(userInfo); }, 1000);
});p.then(data => { console.log('then 1 data:', data); // {name: '张三', userId: '1'}// 根据用户信息获取部门 ID return new Promise((resolve) => { setTimeout(() => { const departId = 1; resolve(departId); // 将 departId 传递给下一个 then }, 1000); });
})
.then(data => { console.log('then 2 data:', data); // 1// 根据部门 ID 获取部门信息 return new Promise((resolve) => { setTimeout(() => { const departInfo = { departId: data,departName: '人事部' }; resolve(departInfo); // 将 departInfo 传递给下一个 then }, 1000); });
})
.then(data => { console.log('then 3 data:', data); // {departId: 1, departName: '人事部'}
});
上面的例子为什么要在 then 中 return new Promise 而不是在 setTimeout 内部中 return?
因为 setTimeout 本身是一个异步函数,它返回的是一个定时器 ID,而不是你函数内部 return 的值。因此,在 setTimeout 中返回的值并不会成为 Promise 链中的下一个值。
2、集中错误处理。在 Promise 链中,任何一步的 .then() 或 .catch() 方法都可以捕获并处理错误。这意味着你可以在整个链中只使用一个 .catch() 来捕获和处理所有之前的异步操作中可能出现的错误,这大大简化了错误处理逻辑。
const p = new Promise((rosolve, reject) => {setTimeout(() => {rosolve('成功') // rosolve}, 1000)
})p.then(data => {console.log('then 1 data:', data); // then 1 data: 成功return 1
})
.then(data => {console.log('then 2 data:', data); // then 2 data: 1throw new Error('抛出异常')return 2
})
.then(data => {console.log('then 3 data:', data); // 没有执行return 3
})
.catch(error => {console.log('catch 3 error:', error); // catch 3 error: Error: 抛出异常
})
现在再来看看开始的那个问题:
“既然 then 方法的第二个参数也可以用于处理 reject 的情况,那为什么还要有 catch 方法呢?为什么要有两个重复的功能?”
-
catch 使代码结构更加清晰,可以很直观的知道这部分代码是专门用于处理错误的,而不用去 then 中找失败回调,它使得链式调用的流程非常流畅
-
then 的失败回调也并非毫无用处,它可以做到局部处理错误且不影响后续的链的运行,也可以根据拿到的具体值,做条件判断,再决定是否要抛出错误。
Promise 静态方法
Promise.resolve(value)
这个方法接收一个值(value)作为参数,并返回一个 Promise 对象。
-
如果
value本身就是一个 Promise 对象,那么返回的 Promise 会“跟随”这个valuePromise 的状态; -
如果
value不是一个 Promise 对象,那么返回的 Promise 会以这个value作为成功的结果(即 fulfilled 状态)。
// 传递 Promise 成功对象
const promise = new Promise(resolve => resolve('成功'));
Promise.resolve(promise).then(data => {console.log(data); // 成功
});-----// 传递 Promise 失败对象
const promise = new Promise((resolve, reject)=> {reject('失败')
});
Promise.resolve(promise).then(data => {},// 或者用 catcherror => {console.log("error:", error, typeof error); // error:失败 string}
);-----// 传递普通值
Promise.resolve(1).then(data => {console.log(data); // 1
});
Promise.reject(reason)
这个方法接收一个原因(reason)作为参数,并返回一个已经处于 rejected 状态的 Promise 对象。这个 reason 会被封装在返回的 Promise 的拒绝原因中。
Promise.reject 与 Promise.resolve 的区别是:
-
Promise.reject 只能返回 rejected 状态的 Promise 对象,而 Promise.resolve 可以返回任意状态的 Promise 对象
-
Promise.reject 的参数就是拒绝原因,而 Promise.resolve 的参数可以理解成是一个 Promise 对象,它包括 PromiseStatus 和 PromiseResult,并以此决定 Promise.resolve 返回的 Promise 对象的状态和结果值。
Promise.reject('失败').catch(data => {console.log(data); // 失败
});-----const promise = new Promise((resolve, reject)=> {reject(1)
});// 这里,Promise.resolve(promise) 接收了一个已经 rejected 的 Promise 对象
// 因此,返回的 Promise 也会是 rejected 的,并且传递相同的拒绝原因(数字 1)
Promise.resolve(promise).then(data => {},error => {console.log("error:", error, typeof error); // 1, number}
);// Promise.reject(reason) 的设计初衷是接收一个拒绝原因(reason),
// 并立即返回一个处于 rejected 状态的 Promise。
// 这里的 reason 应该是任何非 Promise 的值(如字符串、数字、对象等)。
// 虽然技术上可以传入一个 Promise 对象作为 reason,
// 但这并不是 Promise.reject() 的预期用法,也不是一个好的实践。
Promise.reject(promise).then(data => {},error => {console.log("error:", error, typeof error); // 1, object}
);
Promise.all(iterable)
这个方法接受一个可迭代对象(通常是一个数组),其中的每一项都是一个 Promise 对象。
它返回一个新的 Promise 对象,这个 Promise 对象在所有输入的 Promise 对象都成功时才会成功,并且它的结果是一个数组,包含所有输入 Promise 对象的结果;如果其中一个 Promise 对象失败,那么这个新的 Promise 对象会立即失败,并返回那个失败的原因。
const p1 = Promise.resolve(3);
const p2 = 42;
const p3 = new Promise((resolve, reject) => setTimeout(resolve, 100, 'foo'));Promise.all([p1, p2, p3]).then(values => {console.log('values:', values); // [3, 42, 'foo']
}).catch(error => {console.log('error:', error);
});
const p1 = Promise.resolve(3);
const p2 = 42;
const p3 = new Promise((resolve, reject) => setTimeout(reject, 100, 'foo'));Promise.all([p1, p2, p3]).then(values => {console.log('values:', values);
}).catch(error => {console.log('error:', error); // foo
});
拓展:setTimeout(function[, delay, arg1, arg2, ...]);
一旦定时器到期,setTimeout 的第三个及以后的参数会作为第一个 function() 的参数传进去。
所以上面的例子实际上就是 setTimeout(() => {resolve('foo')}, 100)
Promise.all() 在处理多个并发请求的时候非常有效。
这里经常会有一个面试题:如何控制 JavaScript 并发一百个请求?
all、race、any、allSettled
与 all 类似的还有 race、any、allSettled,他们都接收一个数组,数组中的每一项都是一个 Promise 对象,区别如下:
-
all:只有数组中的 Promise 都成功时才进入 then,有一个失败则进入 catch
-
race:数组中最先完成的 Promise(不是按参数顺序),成功则进入 then,失败则进入 catch
-
any:数组中最先成功的 Promise,其 resolve 的内容会进入 then,所有的 Promise 都失败则进入 catch
-
allSettled:数组中所有的 Promise 完成时(无论失败还是成功),会进入 then,永远不会进入 catch
Promise 和 async/await
async
async 用于将一个函数定义为异步函数,且该异步函数的返回值是一个 Promise 对象
const fn = async function() {console.log(1)
}
const p = fn()
console.log(p, p instanceof Promise)
/*
[[Prototype]]: Promise
[[PromiseState]]: "fulfilled"
[[PromiseResult]]: undefined
true
*/
const fn = async function() {console.log(1) // 1return 2
}
const p = fn()
console.log(p, p instanceof Promise)
/*
[[Prototype]]: Promise
[[PromiseState]]: "fulfilled"
[[PromiseResult]]: 2
true
*/
所以 async 可以不需要 await:
const fn = async function() {console.log(1) // 1return 2
};
const p = fn(); // p 是一个 promise 对象
p.then(res => {console.log(res) // 2
});
但是要注意下面这种情况是错误的示范:
const fn = async function() {setTimeout(() => {console.log(1)return 2})
}
const p = fn()
console.log(p, p instanceof Promise)
p.then(res => {console.log('res:', res) // undefined
})
为什么这里的 res 打印的是 undefined?这个问题其实在上面的【链式调用的作用】中有说到过:
因为 setTimeout 本身是一个异步函数,它返回的是一个定时器 ID,而不是你函数内部 return 的值。因此,在 setTimeout 中返回的值并不会成为 Promise 链中的下一个值。
await
await 的作用是等待一个 Promise 对象解决(resolve)或拒绝(reject),它会暂停 await 后面的代码的执行。
-
当 await 等待的 Promise 被解决,会返回解决值
-
当 await 等待的 Promise 被拒绝,会抛出异常
const fn = async function() {console.log(1)return 2
};async function test() {const result = await fn() // 返回解决值console.log("result:", result)
}
console.log("主线程执行中...")
test()/*
主线程执行中...
1
result:2
*/
const p = Promise.reject('失败')async function test() {try {const result = await pconsole.log("result:", result) // 不执行} catch (error) {console.log('异常:', error);}
}
console.log("主线程执行中...")
test()/*
主线程执行中...
异常: 失败
*/
如果 await 等待一个非 Promise 对象会怎样?
async function test() {const result = await 'str'console.log("result:", result)
}
console.log("主线程执行中...")
test()/*
主线程执行中...
result: str
*/
答案:JavaScript 会尝试将该值隐式地转换为一个已解决的 Promise 对象。
为什么 await 一定要在 async 中?
-
语法限制,单独使用 await 会报错
-
await 的作用是等待一个 Promise 对象被解决或拒绝,而 async 函数会隐式地返回一个 Promise 对象
async/await
为什么说 async/await 可以简化 Promise 呢?我们还是使用之前【链式调用的作用】中的一个例子;
先获取用户信息,再根据用户信息获取部门 ID,再根据部门 ID 获取部门信息…
使用 Promise:
const p = new Promise((resolve, reject) => { // 获取用户信息 setTimeout(() => { const userInfo = { name: '张三', userId: '1' }; resolve(userInfo); }, 1000);
});p.then(data => { console.log('then 1 data:', data); // {name: '张三', userId: '1'}// 根据用户信息获取部门 ID return new Promise((resolve) => { setTimeout(() => { const departId = 1; resolve(departId); // 将 departId 传递给下一个 then }, 1000); });
})
.then(data => { console.log('then 2 data:', data); // 1// 根据部门 ID 获取部门信息 return new Promise((resolve) => { setTimeout(() => { const departInfo = { departId: data,departName: '人事部' }; resolve(departInfo); // 将 departInfo 传递给下一个 then }, 1000); });
})
.then(data => { console.log('then 3 data:', data); // {departId: 1, departName: '人事部'}
});
使用 async/await:
async function getUserInfo() {return new Promise((resolve, reject) => {setTimeout(() => {const userInfo = {name: '张三', userId: '1' };resolve(userInfo);}, 1000);});
}async function getDepartId(userInfo) {return new Promise((resolve, reject) => {setTimeout(() => {let departIdif(userInfo.userId == 1) {departId = 1;} resolve(departId);}, 1000);});
}async function getDepartInfo(departId) {return new Promise((resolve, reject) => {setTimeout(() => {let departInfoif(departId == 1) {departInfo = { departId: departId,departName: '人事部' };} resolve(departInfo);}, 1000);});
}async function test() { try { const userInfo = await getUserInfo(); console.log('用户信息:', userInfo);const departId = await getDepartId(userInfo);console.log('部门 ID:', departId);const departInfo = await getDepartInfo(departId);console.log('部门信息:', departInfo);} catch (error) { console.error('发生错误:', error); }
} test()/*
用户信息: {name: '张三', userId: '1'}
部门 ID: 1
部门信息: {departId: 1, departName: '人事部'}
*/
自定义封装 Promise 函数
有助于理解 Promise 原理,可以不看。
class Promise {constructor(executor) {this.PromiseState = 'pending'this.PromiseResult = nullthis.callback = [] //then的参数值容器const that = thisfunction resolve(data) {//保证状态值只能改一次if (that.PromiseState !== 'pending') return//设置状态值为成功that.PromiseState = 'fullfilled'//设置promise结果值that.PromiseResult = data//实现链式调用setTimeout(() => {that.callback.forEach(item => {item.onResolve(data)})})}function reject(data) {if (that.PromiseState !== 'pending') return//设置状态值为失败that.PromiseState = 'rejected'//设置promise结果值that.PromiseResult = datasetTimeout(() => {that.callback.forEach(item => {item.onReject(data)})})}//设置通过throw 改变状态值try {//立即执行保证同步调用(执行器函数)executor(resolve, reject);} catch (e) {reject(e) //e为throw ‘error’ 抛出异常时的值}}//指定原型then方法then(onResolve, onReject) {const that = this//判断回调函数参数 设置参数默认值if (typeof onReject !== 'function') {onReject = reason => {throw reason}}if (typeof onResolve !== 'function') {onResolve = value => value}return new Promise((resolve, reject) => {//封装重复函数function callback(type) {try {let result = type(that.PromiseResult)if (result instanceof Promise) {//if返回对象为promiseresult.then(v => {resolve(v)}, r => {reject(r)})} else {//设置返回值的promise对象成功值resolve(result)}} catch (e) {reject(e) //当抛出异常}}//console.log(this)// 运用箭头函数的原因 这里的this指向函数定义的代码块//设置成功的回调执行if (this.PromiseState === 'fullfilled') {//保证then方法内回调为异步执行setTimeout(() => {callback(onResolve)})}//设置失败回调执行if (this.PromiseState === 'rejected') {setTimeout(() => {callback(onReject)})}//设置改变状态后再执行then回调(对于回调方式改变状态值)if (this.PromiseState === 'pending') {this.callback.push({onResolve: function() { //处理promise异步修改对象状态(实现then回调函数执行在异步回调之后)callback(onResolve)},onReject: function() {callback(onReject)}})}})}//添加catch方法catch (onreject) {return this.then(undefined, onreject)}//给构造函数promise封装resolve 和reject方法static resolve(value) {return new Promise((resolve, reject) => {if (value instanceof Promise) {value.then(v => {resolve(v)}, r => {reject(r)})} else {resolve(value)}})}static reject(reason) {return new Promise((resolve, reject) => {reject(reason)})}//封装all方法static all(values) {return new Promise((resolve, reject) => {let count = 0let arr = []for (let i = 0; i < values.length; i++) {values[i].then(v => {count++arr[i] = vif (count === values.length) {resolve(arr)}}, r => {reject(r)})}})}//封装race方法static race(values) {return new Promise((resolve, reject) => {for (let i = 0; i < values.length; i++) {values[i].then(v => {resolve(v)}, r => {reject(r)})}})}}
Axios
Axios 是一个基于 Promise 的用于发送 HTTP 请求的客户端库。它会返回一个 Promise 对象,代表 HTTP 请求的结果。
并且 Axios 还可以支持请求和响应拦截器、请求取消、HTTP 方法别名、自动转换 JSON 数据等高级功能。
示例:
// get 请求
axios({url: 'http://example.com/resource',method: 'get',params: {id: 1,name: '张三'},headers: {'Content-Type': 'application/json'}
})
.then(response => {console.log(response);
})
.catch(error => {console.error(error);
});// post 请求
axios({url: 'http://example.com/resource',method: 'post',// 使用 data 接收参数data: {id: 1,name: '张三'},headers: {'Content-Type': 'application/json'}
})
.then(response => {console.log(response);
})
.catch(error => {console.error(error);
});
Axios 特点和优势
特点:
-
从浏览器中创建 XMLHttpRequests
- Axios 的浏览器 API 能够发送 XMLHttpRequests,这使得它能够从浏览器中以异步的方式与服务器通信。
-
从 node.js 中创建 HTTP 请求
- 在服务器端,Axios 提供了相似的 API 通过 HTTP 方式与其他服务进行交互。
-
支持 Promise API
- 使用 Promise API 实现请求和响应的同时处理,让异步代码变得简洁,并方便了错误处理。
-
拦截请求和响应
- Axios 的拦截器让你可以在请求发送到服务器前或服务器响应返回应用前更容易地处理它们。
-
转换请求和响应的数据
- 自动转换请求数据和响应数据为 JSON 格式。
-
取消请求
- 提供了通信接口来取消请求,避免不必要的网络活动。
-
统一错误处理
- Axios 使得错误管理和处理集中化,可以对所有 HTTP 请求统一处理异常。
Axios 与 Fetch 的区别
Fetch 示例:
// get 请求
fetch(`http://example.com/resource?id=1&name=张三`, {method: 'GET',headers: {'Content-Type': 'application/json'}
})
.then(response => {// 解析 JSON 响应return response.json();
})
.then(data => {// 处理数据console.log(data);
})
.catch(error => {// 错误处理console.error('Fetch error:', error);
});// post 请求:
fetch(`http://example.com/resource`, {method: 'GET',headers: {'Content-Type': 'application/json'},// 使用 body 接收参数body: JSON.stringify({id: 1,name: '张三'})
})
.then(response => {// 解析 JSON 响应return response.json();
})
.then(data => {// 处理数据console.log(data);
})
.catch(error => {// 错误处理console.error('Fetch error:', error);
});
比较:
-
都支持链式调用,都是基于 Promise。
-
原生
-
Axios 是一个第三方库
-
Fetch 是一个原生的 JavaScript API,即不需要引用
-
-
拦截处理
-
Axios 能够拦截请求和响应。
-
Fetch 不支持请求和响应的拦截。
-
-
取消请求
-
Axios 使用 CancelToken 取消请求。
-
Fetch 使用 AbortController 取消请求。
-
-
错误处理
-
Axios 将任何状态码 >= 200 和 < 300 的响应视为有效响应,其他的都将被拒绝。
-
Fetch 只有在网络故障时或请求被阻止时才会拒绝 promise;如果 HTTP 状态码为 404 或 500,它会将 promise 的状态标记为 resolve,并且需要开发者进行额外检查。
-
-
JSON 数据处理
-
Axios 会自动将请求和响应转为 JSON 格式。
-
Fetch 需要手动将请求转为 JSON 字符串,手动将响应转为 JSON
-
-
处理 Cookie
跨域请求中 Axios 和 Fetch 默认都不会携带 cookie,需要手动设置:
-
Axios 设置
withCredentials: true。 -
Fetch 设置
credentials: 'include'。
-
Axios 响应结构
| 字段 | 类型 | 描述 |
|---|---|---|
| data | any | 服务器响应的数据。Axios 默认会尝试将此数据转换为 JSON 对象 |
| status | number | HTTP 响应的状态码,如 200 表示成功,404 表示未找到等 |
| statusText | string | HTTP 状态码的文本信息,如 OK 或 Not Found |
| headers | Object | 响应头。一个包含所有响应头的对象,键名为响应头的名称,键值为响应头的值。 |
| config | Object | 请求时使用的所有配置选项 |
| request | XMLHttpRequest | 生成当前响应的请求对象,在浏览器中,它是 XMLHttpRequest 实例(几乎不用) |
你可以通过在 .then() 或者 async/await 捕获响应结果来访问这些字段。例如:
axios.get('/some-url').then(response => {console.log(response.status); // 例如:200console.log(response.data); // 服务器端返回的实际数据});async function fetchData() {const response = await axios.get('/some-url');console.log(response.status);console.log(response.data);
}
Axios 请求拦截器(Interceptors)
Axios 拦截器允许开发者在请求被发送到服务器和响应到达客户端之前,对它们进行处理和修改。
请求拦截器的作用
-
添加公共头部。
-
请求数据序列化:在请求发送到服务器之前对请求数据进行处理或转换。
-
设置条件请求:根据特定的条件或业务逻辑取消或更改即将发送的请求。
-
日志记录:记录即将发送的请求的详细信息,方便调试。
-
设置通用参数:例如,添加时间戳参数以防止 GET 请求被浏览器缓存。
响应拦截器的作用
-
统一错误处理:可以在拦截器中捕捉到错误响应,并进行统一的错误处理。例如,根据 HTTP 状态码显示错误信息。
-
数据转换:将响应数据从 JSON 转换为其他格式,或者在数据被传递给 then 或 catch 方法之前,进行预处理。
-
自动刷新 Token:当收到 token 过期的响应时,可以发送请求来刷新 token,然后重试原始请求。
-
性能监控:可以计算请求响应时间,用于应用程序的性能监控。
拦截器例子:
// 添加请求拦截器
axios.interceptors.request.use(function (config) {// 在发送请求之前做些什么config.headers.common['Authorization'] = `Bearer ${token}`;return config;
}, function (error) {// 对请求错误做些什么return Promise.reject(error);
});// 添加响应拦截器
axios.interceptors.response.use(function (response) {// 对响应数据做点什么return response;
}, function (error) {// 对响应错误做点什么if (error.response && error.response.status === 401) {// 处理授权错误}return Promise.reject(error);
});
return Promise.reject 的作用就是中断 Promise 链中的后续 .then() 方法调用,并允许 .catch() 方法捕获到这个错误。
但是要注意 return Promise.reject 并不会中断 HTTP 请求的发送和接收。例如一个 HTTP 请求刚进入 axios.interceptors.request 的时候就立即使用 Promise.reject,那么这个 HTTP 请求还是会被发送到服务器。这与 CancelToken 不同,CancelToken 会取消该请求的发送。
Axios 如何取消请求
使用 CancelToken.source 工厂方法创建取消令牌:
// 1. 引入 axios
const axios = require('axios');// 2. 创建一个取消令牌源
const CancelToken = axios.CancelToken;
const source = CancelToken.source();// 3. 发起请求
axios.get('https://jsonplaceholder.typicode.com/posts', {// 4. 将取消令牌作为请求配置的一部分传递cancelToken: source.token
})// then 中处理请求成功的情况.then(response => {console.log('请求成功:', response.data);})// catch 中处理错误和取消的情况.catch(error => {// 6. 处理请求错误或取消的情况if (axios.isCancel(error)) {console.log('请求取消', error.message);} else {console.error('请求错误', error);}});// 5. 在某个条件下取消请求
// 在 1 秒后取消请求
setTimeout(() => {source.cancel('取消原因');
}, 1000);
总结实现步骤:
-
创建取消令牌源
const CancelToken = axios.CancelToken; const source = CancelToken.source(); -
发起请求时将取消令牌作为请求配置的一部分传递
{cancelToken: source.token,... } -
在某个条件下使用
source.cancel()取消请求source.cancel('取消原因'); -
在 catch() 中使用
axios.isCancel()处理被取消的请求(非必须).catch(error => { if (axios.isCancel(error)) { console.log('请求被取消:', error.message); }... });
Axios 默认配置
如果想为所有的请求统一配置某些设置,可以使用默认配置 axios.defaults:
// 设置全局的 baseURL 默认值
axios.defaults.baseURL = 'https://jsonplaceholder.typicode.com';// 设置全局的 headers 默认值
axios.defaults.headers.common['Authorization'] = 'Bearer your.token.here';// 设置全局的 timeout 默认值
axios.defaults.timeout = 2500;
Axios 中如何创建实例(instance)
创建实例的目的是允许你为该实例的所有请求预定义一些配置项,比如基础 URL、请求头、超时时间等。这样做的好处是在同一个应用中可以重用这一配置,避免每次发送请求时都需要设置相同的配置项。
要创建 Axios 的新实例,你可以使用 axios.create() 方法并传递一个配置对象。以下是创建实例的示例:
// 创建实例
const axiosInstance = axios.create({baseURL: 'https://api.example.com', // 所有请求的基础 URLtimeout: 1000, // 全部请求的超时时间headers: {'X-Custom-Header': 'foobar'} // 全局自定义的请求头
});// 使用实例发起 GET 请求
axiosInstance.get('/users').then(response => {console.log(response.data);});// 使用实例发起 POST 请求
axiosInstance.post('/users', {data: {username: 'example'}
}).then(response => {console.log(response.data);});// 每个实例都能够单独配置拦截器
// 添加请求拦截器
axiosInstance.interceptors.request.use(config => {config.headers['Authorization'] = 'Bearer token'; // 为即将发出的请求动态设置授权头部return config;
});// 添加响应拦截器
axiosInstance.interceptors.response.use(response => {// 处理响应数据return response;
}, error => {// 处理响应错误return Promise.reject(error);
});
默认配置和实例配置的区别:
| 区别 | 默认配置 | 实例配置 |
|---|---|---|
| 作用范围 | 全局,对所有请求生效 | 局部,只对当前实例的请求生效 |
| 配置方式 | 直接修改 axios.defaults | 使用 axios.create() 创建新的实例并配置 |
| 适用场景 | 统一配置所有请求 | 针对不同的请求需要不同配置时使用 |
Axios 性能优化
-
使用缓存避免不必要的请求
-
并发请求
-
使用请求和响应拦截器优化错误处理
-
取消重复请求
-
压缩数据:确保服务器能发送压缩的响应数据(如使用 gzip 或 br 压缩)。这减少了传输的数据量,从而提高了性能。
-
使用 HTTP2:如果服务器支持 HTTP2,使用 HTTP2 可以显著提高性能,因为它提供了头部压缩、多路复用等优势。你可以考虑选择或升级到支持 HTTP2 的服务器。
Axios 处理大型 JSON 数据
设置 responseType: 'stream' 表示获取一个可读的流(stream),然后你可以逐步读取和解析这个流,而不是一次性将整个响应体加载到内存中。这对于大型文件特别有用。
流是原始的二进制数据。你需要自己实现逻辑来将流中的数据转换为 JSON。
axios({method: 'get',url: 'http://example.com/large.json',responseType: 'stream'
})
.then((response) => {// 处理流式响应数据
});
Axios 完整封装示例
// request.js
import axios from 'axios';// 创建一个 axios 实例
const service = axios.create({baseURL: process.env.VUE_APP_BASE_API, // 基础路径timeout: 5000 // 请求超时时间
});// 存储正在进行的请求
const pendingRequests = new Map();
const requestTimestamp = new Map(); // 存储请求的时间戳// 请求拦截器
service.interceptors.request.use(config => {const token = localStorage.getItem('token'); // 假设 token 存储在 localStorageif (token) {config.headers['Authorization'] = `Bearer ${token}`; // 设置 Authorization 头}const requestKey = `${config.method}_${config.url}`;// 获取当前时间戳const currentTime = Date.now();// 检查是否存在已有请求且在1秒内if (pendingRequests.has(requestKey)) {const lastRequestTime = requestTimestamp.get(requestKey);// 只有在1秒内才取消请求if (currentTime - lastRequestTime < 1000) {const cancelTokenSource = pendingRequests.get(requestKey);cancelTokenSource.cancel('Operation canceled due to new request.');}}// 更新时间戳和创建新的 CancelTokenrequestTimestamp.set(requestKey, currentTime);const cancelTokenSource = axios.CancelToken.source();config.cancelToken = cancelTokenSource.token;pendingRequests.set(requestKey, cancelTokenSource);return config;},error => {return Promise.reject(error);}
);// 响应拦截器
service.interceptors.response.use(response => {const requestKey = `${response.config.method}_${response.config.url}`;pendingRequests.delete(requestKey); // 请求完成后删除requestTimestamp.delete(requestKey); // 请求完成后删除时间戳// 根据具体需求处理响应数据return response.data; // 返回数据},error => {const requestKey = `${error.config.method}_${error.config.url}`;pendingRequests.delete(requestKey); // 请求失败后删除requestTimestamp.delete(requestKey); // 请求失败后删除时间戳// 这里可以根据错误状态码做相应处理if (axios.isCancel(error)) {console.log('请求被取消:', error.message);} else {console.error('请求错误:', error);}return Promise.reject(error);}
);export default service;
相关文章:
JavaScript 系列之:Ajax、Promise、Axios
前言 同步:会阻塞。同步代码按照编写的顺序逐行依次执行,只有当前的任务完成后,才会执行下一个任务。 异步:异步代码不会阻塞后续代码的执行。当遇到异步操作时,JavaScript 会将该操作放入任务队列中,继续…...
AI人工智能机器学习之神经网络
1、概要 本篇学习AI人工智能机器学习之神经网络,以MLPClassifier和MLPRegressor为例,从代码层面讲述最常用的神经网络模型MLP。 2、神经网络 - 简介 在 Scikit-learn 中,神经网络是通过 sklearn.neural_network 模块提供的。最常用的神经网…...
鸿蒙开发深入浅出01(基本环境搭建、页面模板与TabBar)
鸿蒙开发深入浅出01(基本环境搭建、页面模板与TabBar) 1、效果展示2、下载 DevEco Studio3、创建项目4、新建页面模板5、更改应用信息6、新建以下页面7、Index.ets8、真机运行9、图片资源文件 1、效果展示 2、下载 DevEco Studio 访问官网根据自己的版本…...
FreeRTOS动态任务和静态任务创建
一.动态任务创建 1.搭建任务框架 去task.c中将任务参数复制到main中 然后将const去掉,它会限制参数类型,任务大小、任务优先级、任务句柄需要去宏定义,任务句柄是指针类型要取地址 vTaskStartScheduler(); //开启任务调度,.c…...
QT:Graphics View的坐标系介绍
在 Qt 的 Graphics View 框架中,存在三种不同的坐标系,分别是 物品坐标系(Item Coordinates)、场景坐标系(Scene Coordinates) 和 视图坐标系(View Coordinates)。这三种坐标系在图形…...
C# httpclient 和 Flurl.Http 的测试
关于C#调用接口或Post,Flurl封装了httpclient, CSDN有哥们提供了一个公网的测试网站,可以测试Post调用,我写了2个函数,测试httpclient和Flurl使用Post: async 和 await 是成对使用的,为了接受web异步返回的数据,winfor…...
精选案例展 | 智己汽车—全栈可观测驱动智能化运营与成本优化
本案例为“观测先锋 2024 可观测平台创新应用案例大赛”精选案例,同时荣获IT168“2024技术卓越奖评选-年度创新解决方案”奖。 项目背景 近年来,中国汽车行业进入转型升级阶段,智能网联技术成为行业发展的核心。车联网、自动驾驶等技术的加速…...
阿里云可观测全面拥抱 OpenTelemetry 社区
作者:古琦 在云计算、微服务、容器化等技术重塑 IT 架构的今天,系统复杂度呈指数级增长。在此背景下,开源可观测性技术已从辅助工具演变为现代 IT 系统的"数字神经系统",为企业提供故障预警、性能优化和成本治理的全方…...
剑指 Offer II 032. 有效的变位词
comments: true edit_url: https://github.com/doocs/leetcode/edit/main/lcof2/%E5%89%91%E6%8C%87%20Offer%20II%20032.%20%E6%9C%89%E6%95%88%E7%9A%84%E5%8F%98%E4%BD%8D%E8%AF%8D/README.md 剑指 Offer II 032. 有效的变位词 题目描述 给定两个字符串 s 和 t ,…...
AcWing 蓝桥杯集训·每日一题2025·密接牛追踪2
密接牛追踪2 农夫约翰有 N 头奶牛排成一排,从左到右依次编号为 1∼N。 不幸的是,有一种传染病正在蔓延。 最开始时,只有一部分奶牛受到感染。 每经过一个晚上,受感染的牛就会将病毒传染给它左右两侧的牛(如果有的话…...
银河麒麟高级服务器操作系统在线调整/pro/{PID}/limits文件中nofile的软限制和硬限制参数值操作方法
银河麒麟高级服务器操作系统在线调整/pro/{PID}/limits文件中nofile的软限制和硬限制参数值操作方法 一 系统环境二 使用场景三 操作步骤 一 系统环境 [rootlocalhost ~]# nkvers ############## Kylin Linux Version ################# Release: Kylin Linux Advanced Server…...
山大软院ai导论实验之采用BP神经网络分类MNIST数据集
目录 实验代码 实验内容 实验代码 import matplotlib.pyplot as plt from matplotlib import font_manager import torch from torch.utils.data import DataLoader import torchvision from torchvision import transforms# 数据预处理 transform transforms.Compose([tra…...
threeJs+vue 轻松切换几何体贴图
嗨,我是小路。今天主要和大家分享的主题是“threeJsvue 轻松切换几何体贴图”。 想象一下,手头上正好有个在线3D家具商店,用户不仅可以看到产品的静态图片,还能实时更换沙发的颜色或材质,获得真实的购物体验。…...
【python】01_写在前面的话
又是爆肝干文的日子,继上次说要出一期Python新手入门教程系列文章后,就在不停地整理和码字,终于是把【基础入门】这一块给写出来了。 不积跬步无以至千里,不积小流无以成江海,一个一个板块的知识积累,早晚你…...
跨平台公式兼容性大模型提示词模板(飞书 + CSDN + Microsoft Word)
飞书云文档 CSDN MD编辑器 Microsoft Word 跨平台公式兼容方案: 一、背景痛点与解决方案 在技术文档创作中,数学公式的跨平台渲染一直存在三大痛点: 飞书云文档:原生KaTeX渲染与导出功能存在语法限制微软Word:Math…...
【Python爬虫(85)】联邦学习:爬虫数据协作的隐私保护新范式
【Python爬虫】专栏简介:本专栏是 Python 爬虫领域的集大成之作,共 100 章节。从 Python 基础语法、爬虫入门知识讲起,深入探讨反爬虫、多线程、分布式等进阶技术。以大量实例为支撑,覆盖网页、图片、音频等各类数据爬取ÿ…...
深入理解 并查集LRUCaChe
并查集&LRUCaChe 个人主页:顾漂亮 文章专栏:Java数据结构 1.并查集的原理 在一些应用问题中,需要将n个不同的元素划分成一些不相交的集合。开始时,每个元素自成一个单元素集合,然后根据一定规律将归于同一组元素的…...
最新版本SpringAI接入DeepSeek大模型,并集成Mybatis
当时集成这个环境依赖冲突,搞了好久,分享一下依赖配置 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instan…...
Effective Python:(17)
Effective Python提供90条python编程技巧和秘籍,对于我们养成良好的编程习惯,减少程序出错非常重要。 这条就是一条很好的建议,即尽量使用抛出异常来处理意外情况,尽量不要用none作为返回值进行判断。问题也比较显然,如…...
3-2 WPS JS宏 工作簿的打开与保存(模板批量另存为工作)学习笔记
************************************************************************************************************** 点击进入 -我要自学网-国内领先的专业视频教程学习网站 *******************************************************************************************…...
React + TypeScript 复杂布局开发实战
React TypeScript 复杂布局开发实战 一、项目架构设计(基于最新技术栈) 1.1 技术选型与工程创建 # 使用Vite 5.x React 19 TypeScript 5.4 npx create-vitelatest power-designer-ui --template react-ts cd power-designer-ui && npm inst…...
滑动验证组件-微信小程序
微信小程序-滑动验证组件,直接引用就可以了,效果如下: 组件参数: 1.enable-close:是否允许关闭,默认true 2.bind:onsuccess:验证后回调方法 引用方式: <verification wx:if&qu…...
Linux 命令大全完整版(12)
Linux 命令大全 5. 文件管理命令 ln(link) 功能说明:连接文件或目录。语 法:ln [-bdfinsv][-S <字尾备份字符串>][-V <备份方式>][--help][--version][源文件或目录][目标文件或目录] 或 ln [-bdfinsv][-S <字尾备份字符串>][-V…...
在VSCode中安装jupyter跑.ipynb格式文件
个人用vs用的较多,不习惯在浏览器单独打开jupyter,看着不舒服,直接上教程。 1、在你的环境中pip install ipykernel 2、在vscode的插件中安装jupyter扩展 3、安装扩展后,打开一个ipynb文件,并且在页面右上角配置内核 …...
IDEA配置JSP环境
首先下载IDEA2021.3,因为最新版本不能简单配置web开发环境。然后新建一个java开发项目: 然后右键创建的项目,添加web框架: 选择web appliciation 在web inf文件夹下创建classes和lib文件夹: 点击file ,选择…...
Idea 中 Project Structure简介
在 IntelliJ IDEA 中,Project Structure(项目结构)对话框是一个非常重要的配置界面,它允许你对项目的各个方面进行详细的设置和管理。下面将详细介绍 Project Structure 中各个主要部分的功能和用途。 1. Project(项…...
旁挂负载分担组网场景
旁挂负载分担组网场景(到路由策略) 1.拓扑 2.需求 使用传统三层架构中MSTPVRRP组网形式VLAN 2—>W3,SW4作为备份 VLAN 3—>SW4,SW3作为备份 MSTP设计—>SW3、4、5运行 实例1:VLAN 2 实例2:VLAN 3 3.配置 交换层 SW3配置 抢占延时ÿ…...
网络安全防御模型
目录 6.1 网络防御概述 一、网络防御的意义 二、被动防御技术和主动防御技术 三、网络安全 纵深防御体系 四、主要防御技术 6.2 防火墙基础 一、防火墙的基本概念 二、防火墙的位置 1.防火墙的物理位置 2.防火墙的逻辑位置 3. 防火墙的不足 三、防火墙技术类型 四…...
Qt 开源音视频框架模块之QtAV播放器实践
Qt 开源音视频框架模块QtAV播放器实践 1 摘要 QtAV是一个基于Qt的多媒体框架,旨在简化音视频播放和处理。它是一个跨平台的库,支持多种音视频格式,并提供了一个简单易用的API来集成音视频功能。QtAV的设计目标是为Qt应用程序提供强大的音视…...
MS SQL 2008 技术内幕:T-SQL 语言基础
《MS SQL 2008 技术内幕:T-SQL 语言基础》是一部全面介绍 Microsoft SQL Server 2008 中 T-SQL(Transact-SQL)语言的书籍。T-SQL 是 SQL Server 的扩展版本,增加了编程功能和数据库管理功能,使得开发者和数据库管理员能…...
