1.Promise对象
解释:Promise 是 JavaScript 中的一个对象,它代表了一个异步操作的最终完成或失败。
(1)出现原因:
因为传统的Ajax请求,会出现一个情况:就像很多个if语句嵌套,导致代码的可读性很差不够直观。
NPC个人理解:类似于我们数据库里面请求打过去拿到一张表里面的 id值,但是不是立即返回这个请求,还要继续根据这个 id 值去 查询到另外一张表里面的 id,有点像主键和外键一样,但是一旦我发送的请求不是一两个,可能请求总个数到达:5,6,7,8,9,到最后面再一层层返回,这样就会导致代码有很多个回调函数嵌套了 ,如下面代码
$.ajax({//第一层Ajax请求
url: '/data1',
success: function(data1) {
console.log('Data 1 received:', data1);
$.ajax({//第二层Ajax请求
url: '/data2',
success: function(data2) {
console.log('Data 2 received:', data2);
$.ajax({//第三层Ajax请求
url: '/data3',
success: function(data3) {
console.log('Data 3 received:', data3);
// 处理所有数据的逻辑
processAllData(data1, data2, data3);
},
error: function(xhr, status, error) {
console.error('Error fetching data 3:', error);
}
});//第3层完成返回
},
error: function(xhr, status, error) {
console.error('Error fetching data 2:', error);
}
});//第2层完成返回
},
error: function(xhr, status, error) {
console.error('Error fetching data 1:', error);
}
});//第1层完成返回
function processAllData(data1, data2, data3) {
// 在这里处理从三个 AJAX 请求获取的所有数据,
//类似于我上面说的数据库里面一张表 关联 另外一张表,然后进行Ajax嵌套
console.log('Processing all data:', data1, data2, data3);
}
(2)原始的Promise代码:
从上面看的Ajax嵌套来看,代码直观性很差,这种情况的出现也就引出了:promise
类似于函数的调用链了,避开了嵌套,代码如下:
let p = new Promise((resolve,reject)=>{
$.ajax({
//相对路径: ./表示当前目录, ../表示上层目录, 可以多次使用../
url : "../data/Me.json",
success(reslutData){//resultData就是Ajax返回的数据本身
console.log(reslutData);
// 这里别写成:resolve.resultData
//这句话类似于开关,让我们执行成功之后,继续发送请求的开关
resolve(reslutData);
},
error(err){//err同理也是Ajax返回的数据本身
console.log(err);
reject(err); // 添加 reject(err) 语句
}
})
})
p.then((resultData)=>{//上一个promise对象success后,需要执行的代码
//注意这里要有return,否则下面的catch里面的代码无法执行
return new Promise((resolve,reject)=>{
$.ajax({ //这里故意让请求的URL写错,让return之后将错误能够正确被捕获
url : `../dat1a/Me_${resultData.id}.json`,
success(reslutData) {
console.log(reslutData);
resolve(resultData);
},
error(err) {
console.log(err);
reject(err); // 添加 reject(err) 语句
}
})
})
})
.catch((err) => {
console.log("catch语句捕获到了:", err);
})
resolve(resultData)
:这个语句用于在 Promise 中成功地完成一个异步操作,相当于是一个开关,有它才会执行 then语句。
Promise 链中的 then()
方法是接收resultData
数据,并对其进行进一步的处理。
reject(err)
:这个语句用于在 Promise 中发生错误时,通知 Promise 链中的 catch()
方法。其进行进一步的处理
(3)为什么要return promise对象?
现象:当有两个Ajax请求或者 更多的时候,第二个Ajax请求要是不 return,就不会执行catch里面的代码:
原因:
- 有
return
:新创建的 Promise 被返回并添加到 Promise 链中,错误会沿着链向下传播,最终被catch
方法捕获。 - 没有
return
:新创建的 Promise 没有被返回,对Promise 链没有任何影响,错误也不会顺着链传播,因此catch
方法不会捕获到这个错误。
(5)promise之代码重排
于晏人话:就是对promise进行了一定的封装优化,这一次就非常直接和清晰了
学到后边,但是发现这样的promise链式调用,还是有点点复杂,但是相比于原来的Ajax嵌套好太多,为了更好的使用promise链式调用,于是就出现了promise代码重排,目的就是让代码:更直观,可读性会更好:
我们看上面的每一次promise的 Ajax请求 里面有很多的代码是重复的,代码冗余:
我们明白用promise发起Ajax请求的结果,就是为了拿到一个promise对象?
所以就有了一个思路:我们将这个得到promise对象的过程,封装成一个方法。如下:
//自己封装一个方法,目的就是为了得到一个promise对象 function get(url,data){
function get(url,data){
return new Promise(
(resolve,reject)=>{
$.ajax({
url : url,
data : data,
success(resultData){
resolve(resultData);
},
error(err){
reject(err);
}
})
})
}
下面就是在封装的基础上实现发送多次请求:
(其实也不高级,就是为了让我们方便看,直观地去处理业务逻辑)
// 个人理解的模版:
//promise是then方法的调用者,
// 其中resultData当前promise携带的数据
// 在then方法内部继续创建promise对象,并传递(return)
get("../data/Me.json").then(
(resultData)=>{
console.log(resultData);//打印第一次请求的数据,并返回第二次的Ajax请求的promise对象
return get(`../data/Me_${resultData.id}.json`);
}//得到新的promise对象
).then(
(resultData)=>{
console.log(resultData);//打印第2次请求的数据,并返回第3次的Ajax请求的promise对象
return get(`../data/Me_${resultData.friend_id}.json`);
}
).then(
(resultData)=>{
console.log(resultData)//打印第3次请求的数据
}
)
浏览器里面控制台输出的结果:
2.模块化编程
只介绍两个时期的:一个ES5, 一个ES6的
模块化编程的人话理解:类似于封装 java 一个类里面的所有属性,到另外一个类里面去用,
场景:当我们有多个js文件一号js文件需要调用2号js文件里面的:对象、变量、常量、函数,
类比:html文件里面 使用js文件里面的东西:
本质把一个js文件封装成对象,最后在另外一个js文件里面 拿这个对象来使用(个人理解,狗头保命= 。=)
看代码:
<1> ES5模块化的两句重要代码:
导出和导入
(1)导出的代码:
三种方式很简单(最后一种是可以取别名的方式)
module.exports = {
Me,
YOU,
Me_words,
You_words,
add
};
//或者这样,两种方式都可以写,少了个module
exports = {
Me,
YOU,
Me_words,
You_words,
add
};
//第三种:如果导出的时候想要取一个自己 应用的别名:
exports = {
别名1:Me,
别名2 :YOU,
me_words :Me_words,
you_words :You_words,
sum : add
};
(2)导入的代码:
const Dui_Xiang = require("./A");
//模版:const 对象名 = require("引入的js文件路径");
(3)只导出某一部分属性
如果只是想要导出某一部分属性(不想导出全部):
导出端:
module.exports = {
Me,
You_words,
add
};
接收端:
const { Me, You_words, add } = require("./A");
相对而言很灵活的:自己别太笨,看情况使用就行
<2> ES6模块化导出导入:
ES6的导出也是有三种方式:
(1)第一种:批量导出+导入
其实我个人觉得和ES5的没什么区别,就是关键字变了变成export,用法都一样
export {//ES5的是module.exports{} 或 exports{}
PI,
E,
add,
subtract,
Calculator
};
批量导出对应的 导入:
(代码最后是对应,怎么使用该属性的格式)
import {PI, E, add } from 'js文件路径';
//如果对方 导出了 三个属性,
// 导入这边 也可以选择性接受一个或者两个,不用全部接受
//导入进来属性使用的格式:
console.log(PI);
console.log(E);
console.log(add(2, 3));
(2)第二种:直接导出+导入
人话:刚定义好,我就给你导出了
代码如下:
//在我定义语句前面 直接 + 一个export,
//相当于然后那个变量 “直接导出”
//只有写了export 的那些变量会被导出,其余无影响
export const PI = 3.14159;
export function add(a, b) {
return a + b;
}
对应的导出代码:(其实和上面第一种导出方式很像,只是这个你要与导出的属性对应【属性名也要相同哈,别自己取名字,彦祖!】,不要多,也不要少)
import { PI, add } from 'js文件路径';
console.log(PI);
console.log(add(2, 3));
(3)第三种:默认导出+导入
人话+个人理解:这个就是把要导出的属性 包装成一个 对象,通过这个对象来取属性,这个导出类似于ES5里面的导入
//默认导出:导出了一个对象(导入的时候自己取名)
//这个对象里面有 add,subtract两个方法
export default {
add(a, b) {
return a + b;
},
subtract(a, b) {
return a - b;
}
};
导入代码:
//默认的导入
import 自定义的对象名 from 'js文件路径';
//使用属性的格式:
console.log(自定义对象名.add(2, 3)); // Output: 5
console.log(自定义对象名.subtract(5, 3)); // Output: 2
注意细节补充:
1.ES6的模块化不能在Node.js中执行,要先用Babel转码成ES5之后才可以进行处理
2.export 不仅可以导出对象,比如:基本类型变量、函数、数组、对象都可以导出
3.es6有导出方式较多,不同的导出方式对导入方式也有一定影响