前言
接触promise有一段时间了,不管是自己从书上、看视频教学、看他人的博客记录,感觉都没有很细致的了解promise,promise到底是干嘛的?为什么有resolve、reject还要有then、catch、Promise.resolve等函数?我到底怎么才能用到promise?为什么面试的时候需要我写一个promise? 这些都是我遇到的一些问题,下面是我微薄的见解与记录,希望看完能帮到读者一点点。
promise的初始了解
promise基本概念
promise是ES6提出来的,promise本身是同步的,它可以包裹异步请求,解决了用ajax产生的回调地狱问题,同时async函数返回的也是一个promise对象、await后面如果接的是promise对象则直接返回该值、axios也是基于promise进行封装的。
promise其实就是返回的一个状态标记,里面可以放同步方法,也可以放异步方法,改变状态的是三种,可以理解为后续再去通过回调方法去把成功或失败的值拿到,或者利用。
promise,中文就是承诺,其创建的函数初始状态为pending,改变后有两种状态成功(resolve)、失败(reject),当状态改变后就不允许第二次的状态改变,一般是把 异步操作放在promise中
Promise的状态与值介绍
1、Promise的状态介绍
实列对象中的一个属性:PromiseState
PromiseState:Pending、resolved、rejected
改变前的状态是Pending,只能改变为resolved、rejected,resolved、rejected之间不能互相改变,只能pending向它们改变
2、promise对象中的值
实列中对象的另一个属性:PromiseResult
保存着成功/失败的结果
创建一个promise
<script>
var promise = new Promise((resolve, reject) => {
//异步操作
//成功的结果调用resolve函数
resoleve()
//失败的结果调用reject函数
reject()
});
</script>
可以看到Promise可以通过new方法来得到,也就是构造函数,我们看下promise对象中包含什么?
可以看出,创建变量名为promise其实本质就是一个promise对象,千万不要觉得这里很无聊,我们只有了解最简单的开始后面才会理解。
promise对象中的内容:
1、PromiseStatus:保存的状态,如果在promise函数内没给状态改变,就为pending
2、PromiseValue:保存的值,如果没有赋值就为undefined
3、promise中接受一个函数参数,参数中携带reject、resolve两个状态
4、promise可以包裹异步操作(也是对异步任务的封装,操作成功返回resolve,失败返回reject)
5、then放方法接受两个参数,而且两个参数都是函数类型的值,第一个参数是对象成功时候的回调,第二个参数是对象失败的时候回调
举个列子:
<script>
var p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("ok");
}, 1000);
});
console.log(p1);
</script>
创建了一个promise对象为p1,其在promsie中包裹了一个异步方法,我们把p1的状态改变为成功的,打印出p1看看内容是什么?
可看出p1是一个promise对象,状态为resolve,值为ok
promsie中的方法
then
then方法是promise实列化的方法,它接受两个函数参数,分别对应着promise的成功、失败,是一个回调函数,可以通过拿到promise对应状态下的值,如果只有一个参数,则只返回成功的值,如下所示:
我们写一个按钮,点击后30%的概率弹出中奖提示:
HTML文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<link href="./index.css" rel="stylesheet" />
</head>
<body>
<div class="content">
<button class="content_buton">
<p class="content_p">抽奖</p>
</button>
</div>
<script>
function randomNum(m, n) {
var num = Math.floor(Math.random() * (m - n) + n);
return num;
}
let timer = null; //防抖操作,定义一个值
const btn = document.getElementsByClassName("content_buton")[0];
btn.addEventListener("click", () => {
var promise = new Promise((resolve, reject) => {
clearTimeout(timer); //清除掉上一次的值
timer = setTimeout(() => {//这里不管点击多少次,都在一秒的时间内只执行一次
const time = randomNum(1, 100);
if (time <= 30) {
resolve(time);
} else {
reject(time);
}
}, 1000);
});
promise.then(
(value) => {
console.log("value", value);
alert("中奖了,你的数字为:", value);
},
(value) => {
alert("再接再厉,你的数字为:", value);
}
);
});
</script>
</body>
</html>
css文件
.content_buton {
width: 100px;
border-radius: 10px;
background-color: #1e90ff;
border: 0px;
cursor: pointer;
}
.content_p {
font-size: larger;
}
在这里如果成功做一个状态改变也就是调用resolve函数,失败就调用reject函数,然后用then函数做出对应的处理
上面的列子中还做出了简单的防抖操作。
注意:then返回的结果是一个promise对象,既然是promise对象,那有如下规则
1、若参数是非promise对象则反回的是成功的状态的值
2、若参数是promise对象,则返回的值由参数中promise决定
主要看接受的参数什么类型
如下列:
<script>
var p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(
new Promise((resolve, reject) => {
return reject("ok");
})
);
}, 1000);
});
p1.then((v) => {
console.log(v);
});
p1.catch((v) => {
console.log(v);
});
</script>
catch
catch回调函数只能接受一个参数,传入的参数只能是promise的失败状态,用法和then差不多,把上面的列子改一下:
<script>
function randomNum(m, n) {
var num = Math.floor(Math.random() * (m - n) + n);
return num;
}
let timer = null;
let baodi = 0;
const btn = document.getElementsByClassName("content_buton")[0];
btn.addEventListener("click", () => {
var promise = new Promise((resolve, reject) => {
clearTimeout(timer);
timer = setTimeout(() => {
const time = randomNum(1, 100);
if (time <= 30) {
resolve(time);
} else {
reject(time);
}
}, 1000);
});
promise.then((value) => {
console.log("value", value);
alert("中奖了,你的数字为:", value);
});
promise.catch((value) => {
alert("再接再厉,你的数字为:", value);
});
});
</script>
Promise.resolve
该方法不属于实列对象的方法,而是promise函数对象上的方法,规则如下:
1、若果传入的的参数为非promise类型对象,则返回的结果是成功的promise对象
2、如果传入的是promise对象,则参数的结果决定了resolve的结果
3、参数接受一个,非数组
作用:快速创建一个promise对象,而且可以指定一个封装的值
<script>
let p1 = Promise.resolve(111);
//若果传入的的参数为非promise类型对象,则返回的结果是成功的promise对象
// 如果传入的是promise对象,则参数的结果决定了resolve的结果
let p2 = Promise.resolve(
new Promise((resolve, reject) => {
// 这里就是内部的promise中reject的结果决定了promise.resolve的结果
reject("ok");
})
);
console.log(p1);
console.log(p2);
</script>
在p2中我们可以看到,该方法下返回的是一个promise对象,返回的值也是由参数决定的
Promise.reject
该方法返回一个失败的promise对象,和resolve类似,但是不管参数是传入非promise对象还是promise对象,返回的都是一个失败的promise对象
<script>
let p1 = Promise.reject(111);
//若果传入的的参数为非promise类型对象,则返回的结果是成功的promise对象
// 如果传入的是promise对象,则参数的结果决定了resolve的结果
let p2 = Promise.reject(
new Promise((resolve, reject) => {
// 这里就是内部的promise中reject的结果决定了promise.resolve的结果
resolve("ok");
})
);
console.log(p1);
console.log(p2);
</script>
作用就是快速的返回一个指定值失败的promsie对象
Promise.all
all方法和之前的方法不同,它的参数接收的是一个数组,Promise.all(value),value是一个包含n个promise的数组
1、all方法只有传入的数组都是resolve状态的才会返回成功,
2、若传入不成功的promsie则只返回第一个不成功的proimsie(就算失败的前有成功的状态,也只返回失败的值)
3、其中promsie执行的顺序会按照传入参数的promise顺序进行执行(p1比p2先传入,就算p1的运行时间比p2长,在都成功的情况下,也会先返回p1的结果)
在这里举列子中,resolve、reject的方法就派上用处了,快速的创建指定状态的promise对象
<script>
let p1 = Promise.reject("111");
//若果传入的的参数为非promise类型对象,则返回的结果是成功的promise对象
// 如果传入的是promise对象,则参数的结果决定了resolve的结果
let p2 = Promise.reject(
new Promise((resolve, reject) => {
// 这里就是内部的promise中reject的结果决定了promise.resolve的结果
resolve("ok");
})
);
let p3 = Promise.resolve("888");
let p4 = Promise.resolve(
new Promise((resolve, reject) => {
resolve("hellow");
})
);
console.log(
"promise.all返回失败的状态",
Promise.all([p3, p1, p3, p2, p4])
);
console.log("promise.all返回成功的状态", Promise.all([p3, p4]));
</script>
Promise.race
race和all接受的参数一样,需要是一个数组,但是它返回的值 是第一个完成的promise的状态的值 ,如下:
<script>
let p1 = Promise.reject("111");
//若果传入的的参数为非promise类型对象,则返回的结果是成功的promise对象
// 如果传入的是promise对象,则参数的结果决定了resolve的结果
let p2 = Promise.reject(
new Promise((resolve, reject) => {
// 这里就是内部的promise中reject的结果决定了promise.resolve的结果
resolve("ok");
})
);
let p3 = Promise.resolve("888");
let p4 = Promise.resolve(
new Promise((resolve, reject) => {
resolve("hellow");
})
);
let p5 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve();
}, 1000);
});
console.log("promis.race返回的值", Promise.race([p5, p1, p2]));
console.log("promis.all返回的值", Promise.all([p5, p3, p4]));
console.log("promis.race返回的值", Promise.race([p2, p5, p2]));
</script>
可以看到返回的结果并不是根据排序来的,是根据先完成的值来的
promsie中的异常穿透
当我们使用很多的then去回调时,如果有throw抛出异常,我们可以用catch去拿到报错
<script>
let p1 = new Promise((resolve, reject) => {
resolve();
});
p1.then(() => {
throw "失败";
})
.then(() => {
console.log(222);
})
.then(() => {
console.log(33);
})
.catch((err) => {
console.log(err);
});
</script>
实际上就是链式调用最后加上一个catch,第一个报错就会再catch中被拿到
在链式调用时,当promise的状态没改变的时候,函数不会下一步执行,会中断操作
Promsie自定义封装
index.js文件
function Promise(excutor) {
this.PromiseStatus = "pending";
this.PromiseResult = null;
this.callback = [];
const resolve = (data) => {
if (this.PromiseStatus === "pending") {
this.PromiseResult = data;
this.PromiseStatus = "resolve";
setTimeout(() => {
this.callback.forEach((item) => {
item.OnResolve(data);
});
});
}
};
const reject = (data) => {
if (this.PromiseStatus === "pending") {
this.PromiseResult = data;
this.PromiseStatus = "reject";
setTimeout(() => {
this.callback.forEach((item) => {
item.OnReject(data);
});
});
}
};
try {
excutor(resolve, reject);
} catch (e) {
reject(e);
console.log(reject(e));
}
excutor(resolve, reject);
}
Promise.prototype.then = function (OnResolve, OnReject) {
// 这步允许只穿一个参数,可以把不传的另一个参数抛出
if (typeof OnReject !== "function") {
OnReject = (reason) => {
throw reason;
};
}
if (typeof OnResolve !== "function") {
OnReject = (value) => {
throw value;
};
}
return new Promise((resolve, reject) => {
//封装函数
const callback1 = (type) => {
try {
let result = type(this.PromiseResult);
if (result instanceof Promise) {
result.then(
(v) => {
resolve(v);
},
(v) => {
reject(v);
}
);
} else {
resolve(result);
}
} catch (e) {
reject(e);
}
};
//调用then的回调函数
if (this.PromiseStatus === "resolve") {
setTimeout(() => {
callback1(OnResolve);
});
}
if (this.PromiseStatus === "reject") {
setTimeout(() => {
callback1(OnReject);
});
}
//判断pending的状态,保存回调函数
if (this.PromiseStatus === "pending") {
this.callback.push({
OnResolve: () => {
try {
let result = OnResolve(this.PromiseResult);
if (result instanceof Promise) {
result.then(
(v) => {
resolve(v);
},
(v) => {
reject(v);
}
);
} else {
resolve(result);
}
} catch (e) {
reject(e);
}
},
OnReject: () => {
try {
let result = OnReject(this.PromiseResult);
if (result instanceof Promise) {
result.then(
(v) => {
resolve(v);
},
(v) => {
reject(v);
}
);
} else {
resolve(result);
}
} catch (e) {
reject(e);
}
},
});
}
});
};
Promise.prototype.catch = function (OnReject) {
return this.then(undefined, OnReject);
};
//自定义Promise.resolve方法,感觉和then差不多
Promise.resolve = (value) => {
return new Promise((resolve, reject) => {
if (value instanceof Promise) {
value.then(
(v) => {
resolve(v);
},
(v) => {
reject(v);
}
);
} else {
resolve(value);
}
});
};
Promise.reject = (value) => {
return new Promise((resolve, reject) => {
return reject(value);
});
};
Promise.all = (value) => {
return new Promise((resolve, reject) => {
console.log("111", value.length);
let data = [];
let temp = 0;
for (let i = 0; i < value.length; i++) {
value[i].then(
(v) => {
temp++;
console.log(temp);
data[i] = v;
if (temp === value.length) {
resolve(data);
}
},
(v) => {
reject(v);
}
);
}
});
};
Promise.race = (value) => {
return new Promise((resolve, reject) => {
for (let i = 0; i < value.length; i++) {
value[i].then(
(v) => {
resolve(v);
},
(v) => {
reject(v);
}
);
}
});
};
HTML文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<script src="./index.js"></script>
<title>Document</title>
</head>
<body>
<script>
let p1 = new Promise((resolve, reject) => {
// resolve("ok");
reject("oo");
// throw '错误'
// 》》》》》》》》》》》 上面的都是同步的去改变对象的状态,下面举出异步的改变对象的状态
// setTimeout(() => {
// reject("ok");
// }, 1000);
});
// console.log("p1", p1);
// const res = p1.then(
// (e) => {
// return "yhear";
// },
// (e) => {
// throw "o";
// }
// );
let res = Promise.resolve(
new Promise((resolve, reject) => {
resolve("ooo");
})
);
let a = Promise.resolve("ok");
let b = Promise.reject("false");
let result = Promise.race([b, a, res]);
console.log(result);
</script>
</body>
</html>