在 JavaScript 的异步编程领域的一个强大的工具:promise。能够有效的管理各种异步操作,让原本混乱的代码变的井井有条。不管是在工作中,还是在面试的时候,promise也是一个常被用到和被提问到的点,那么让我们进行一个探究,写一个简单版的promise,了解其中的逻辑。
了解promise
作用:
- 用来解决回调地狱的
- 利用的是then方法的链式调用
promise实例的特点:
- 三种状态(pending等待状态、Fulfilled已完成状态、Rejected已拒绝状态)
- 三个方法(then,catch,finally)还有一些静态方法(all,allsettled等等)
promise对象两个属性
- promiseResult(表示异步返回的值)
- promiseState(当前promise对象的状态,默认是pending)
promise回调函数接收两个参数
- resolve
- reject
日常开发实际情况:
- async + await + promise搭配使用
- promise不是最佳的解决回调地狱的方案,因为其本身在使用回调
- async + await可以摆脱回调,最终能做到的将异步的任务转换成同步的流程
promise解决回调地狱问题的小案例
function top1(callback) {
setTimeout(() => {
callback('数据1');
}, 1000);
}
function top2(data1, callback) {
setTimeout(() => {
callback(data1 + '数据2');
}, 1000);
}
function top3(data2, callback) {
setTimeout(() => {
callback(data2 + '数据3');
}, 1000);
}
top1((data1) => {
top2(data1, (data2) => {
top3(data2, (finalData) => {
console.log(finalData);
});
});
});
对于上面这个代码不知道大家有没有跟我一样,第一次看,单个代码很好理解,但是下面这个是个什么逻辑,不太好理解。对于这种情况,下面有对这个代码进行一个解析。但是同时我们也看出其复杂之处。 使用promise进行修改后可以说结构化非常的清晰。希望通过这样的一个例子可以加深对它的理解。
function top1() {
return new Promise((resolve) => {
setTimeout(() => {
resolve('数据1');
}, 1000);
});
}
function top2(data1) {
return new Promise((resolve) => {
setTimeout(() => {
resolve(data1 + '数据2');
}, 1000);
});
}
function top3(data2) {
return new Promise((resolve) => {
setTimeout(() => {
resolve(data2 + '数据3');
}, 1000);
});
}
top1()
.then((data1) => top2(data1))
.then((data2) => top3(data2))
.then((finalData) => console.log(finalData));
手写一个简单的promise
弄清楚上面的代码,了解到函数变量的知识,我们可以去研究promise的手写代码【此代码比较简单,不完善,旨在理解promise的相关过程】
function Promise(callback) {
this.promiseResult = undefined;
this.promiseState = "pending";
const _this = this;
//resolve函数
function resolve(value) {
if (_this.promiseState === "pending") {
_this.promiseState = "fulfilled";
_this.promiseResult = value;
}
}
//reject函数
function reject(error) {
if (_this.promiseState === "pending") {
_this.promiseState = "rejected";
_this.promiseResult = error;
}
}
//回调函数
callback(resolve, reject);
}
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(1);
}, 2000);
});
相比起上面的案例,这段代码应该是比较清晰的
1.首先根据promise的返回值,我们在封装时候,先将promiseResult = undefined;promiseState = "pending";建立一个初始的状态。
2.封装了两个resolve和reject函数(用于修改状态和赋值)
-
callback() 函数就是 (resolve, reject) => { setTimeout(() => { resolve(1); }, 2000); } 此时的resolve和reject只是变量。内容是promise内部的封装的
-
最后
callback(resolve, reject)输出结果,此时的resolve和reject是两个函数传递给了callback里面,而setimeout里面调用了resolve(1)函数执行了我们在promise里面封装的resolve函数
案例代码解析
函数变量
我们可以先了解 函数变量 的使用,方便后续理解。
函数变量:顾名思义就是把一个函数当作变量
简单的使用:函数b作为变量传给了 a函数,并在a中作为callback()触发
function a(num, callback) {
console.log("there is a number : " + num + "!");
callback();
}
function b() {
console.log("yes!");
}
// 调用 a函数,并传入两个参数:字符串 123 和函数 b
a(123, b);
另外的写法:初学时可能被另外写法混淆【需要注意:当内容多了容易混淆】
function a(num, callback) {
console.log("there is a number : " + num + "!");
callback();
}
// 调用 a函数,并传入两个参数:字符串 123 和函数 b
a(123, function b() {
console.log("yes!");
});
// 调用 a函数,并传入两个参数:字符串 123 和函数 b
a(123, ()=>{
console.log("yes!");
});
代码分解
根据我们的前提,()=>{}这样的一个结构就是传了一个函数,然后我们将这个代码进行分解
top1(
(data1) => {
top2(data1, (data2) => {
top3(data2, (finalData) => {
console.log(finalData);
});
});
}
// 这整个就是传给top1的 callback函数
//而top1函数里面的 callback('数据1')的'数据1'就是data1
);
==========将它换一种写法(代入进去)============
top1(callback){ //这里我还是用callback代替方便查看
setTimeout(() => {
//------------------------------------
('数据1') => {
top2(data1, (data2) => {
top3(data2, (finalData) => {
console.log(finalData);
});
});
}
//-------------------------------------
}, 1000);
}
根据上面这个代码的分解,依次类推,我们可以把里面的 ()=>这种形式进行替代,方便我们的理解查看【以下代码写法是有问题的,只是方便理解这个过程】
top1(callback){
// top1
setTimeout(() => {
// top1的callback('数据1')
('数据1') => {
top2(data1, callback){
// top2
setTimeout(() => {
// top2的callback(data1 + '数据2')
(data1 + '数据2') => {
top3(data2, callback){
// top3
setTimeout(() => {
// top3的callback(data2 + '数据3')
(data2 + '数据3') => {
console.log(data2 + '数据3');
}
}, 1000);
};
}
}, 1000);
};
}
}, 1000);
}