网站美工要求建设零食网站的可行性

当前位置: 首页 > news >正文

网站美工要求,建设零食网站的可行性,竞价推广招聘,上海app搭建前言 同步#xff1a;会阻塞。同步代码按照编写的顺序逐行依次执行#xff0c;只有当前的任务完成后#xff0c;才会执行下一个任务。 异步#xff1a;异步代码不会阻塞后续代码的执行。当遇到异步操作时#xff0c;JavaScript 会将该操作放入任务队列中#xff0c;继续…前言 同步会阻塞。同步代码按照编写的顺序逐行依次执行只有当前的任务完成后才会执行下一个任务。 异步异步代码不会阻塞后续代码的执行。当遇到异步操作时JavaScript 会将该操作放入任务队列中继续执行后续的同步代码直到同步代码执行完毕再从任务队列中取出异步操作的结果进行处理。
Ajax、Promise、Axios 他们都是异步相关的技术。 Ajax 用来发起网络请求 Promise 是 JavaScript 的一种异步编程解决方案不仅限于异步网络请求可以使用 Promise 来封装 Ajax 网络请求 Axios 就是使用 Promise 来封装 Ajax 请求的。
Ajax AjaxAsynchronous 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?nameJohnage30, 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 或 rejectthen 和 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 的成功回调函数捕获 // 情况 1then 的失败回调捕获 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);} );// 情况 2catch 捕获 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: 失败 //})// 情况 2return 了 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 会“跟随”这个 value Promise 的状态 如果 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 strconsole.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 pendingthis.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®})} 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®})} 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 {countarr[i] vif (count values.length) {resolve(arr)}}, r {reject®})}})}//封装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®})}})}}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?id1name张三, {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 响应结构 字段类型描述dataany服务器响应的数据。Axios 默认会尝试将此数据转换为 JSON 对象statusnumberHTTP 响应的状态码如 200 表示成功404 表示未找到等statusTextstringHTTP 状态码的文本信息如 OK 或 Not FoundheadersObject响应头。一个包含所有响应头的对象键名为响应头的名称键值为响应头的值。configObject请求时使用的所有配置选项requestXMLHttpRequest生成当前响应的请求对象在浏览器中它是 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;