Promise基础
回调地狱
在编写js异步代码的时候,我们经常会遇到,必须要等到得到数据或者反馈之后,才能进行下一步的操作,不然,整个代码就不能执行,也就是说,我们的代码,必须要放在异步函数的中才行,比如下面的代码:
let users = null;
Mock.mock(/users/,"get",function(){
return Mock.mock({
"users|10-15":[{
"username":"@cname()",
"sex|1":["男","女"],
"tel":/^1[3-9]\d{9}$/,
"score|1-100":1
}]
}).users
})
let xhr = new XMLHttpRequest();
xhr.open("get","/users",false);
xhr.send();
xhr.onreadystatechange = function(){
if(xhr.readyState === 4 && xhr.status === 200){
let txt = xhr.responseText;
users = JSON.parse(txt);
//如果数据是ajax异步获取的
//那么处理数据的操作,就必须放在ajax的异步操作中
setHTML(users);
}
}
function setHTML(users){
for (let i = 0; i < users.length; i++) {
let user = users[i];
let str = `<tr>
<td>${user.username}</td>
<td>${user.sex}</td>
<td>${user.tel}</td>
<td>${user.score}</td>
</tr>`
mybody.innerHTML += str
}
}
//这里直接调用会报错...
//因为数据是通过ajax异步获取的,这里是同步代码
//users在这个时候还没有值
// setHTML(users);
这是我们以前已经写过的一个代码,很明显,如果在ajax回调函数以外不能获取数据,只能把操作放入到回调函数中进行。
当然,这么看你觉得还行,但是如果这个操作之后,还需要再进行下一步的异步操作呢?
比如,我们模拟一个求职的过程,当你发出求职信,等待企业的回复,如果拒绝了就继续再投出下一家的求职信,如果再拒绝了,再投...
function sendMessage(company,onFulfilled,onReject) {
console.log(`向 ${company} 公司提交了求职信!`);
console.log("......等待回复中......")
setTimeout(function () {
if (Math.random() >= 0.1) {
if (Math.random() >= 0.5) {
onFulfilled({
pass: true,
msg: `恭喜你,通过了${company}公司的面试`
})
}
else {
onFulfilled({
pass: false,
msg: `对不起,${company}拒绝了你的面试申请`
})
}
}
else {
if (Math.random() >= 0.5) {
onReject({
code: 1,
msg: "你的网络出错,发不了求职信了"
})
}
else {
onReject({
code: 2,
msg: "你的征信有问题,直接被拉黑"
})
}
}
}, 1000);
}
sendMessage("华为", (reply) => {
if (reply.pass) {
console.log(reply.msg);
}
else {
sendMessage("小米", (reply) => {
if (reply.pass) {
console.log(reply.msg);
}
else {
sendMessage("京东", (reply) => {
if (reply.pass) {
console.log(reply.msg);
}
else {
sendMessage("天美", (reply) => {
if (reply.pass) {
console.log(reply.msg);
}
else {
console.log("...休息一下")
}
}, (err) => {
console.log(err.msg);
})
}
}, (err) => {
console.log(err.msg);
})
}
}, (err) => {
console.log(err.msg);
})
}
}, (err) => {
console.log(err.msg);
})
这一层一层的回调嵌套,形成了传说中的「回调地狱 callback hell」
要解决这样的问题,需要Promise出马
Promise规范
Promise是一套专门处理异步场景的规范,它能有效的避免回调地狱的产生,使异步代码更加清晰、简洁、统一
这套规范最早诞生于前端社区,规范名称为Promise A+
该规范出现后,立即得到了很多开发者的响应
Promise A+ 规定:
- 所有的异步场景,都可以看作是一个异步任务,每个异步任务,在JS中应该表现为一个对象,该对象称之为Promise对象,也叫做任务对象
- 每个任务对象,都应该有两个阶段、三个状态
根据常理,它们之间存在以下逻辑:
- 任务总是从未决阶段变到已决阶段,无法逆行
- 任务总是从挂起状态变到完成或失败状态,无法逆行
- 时间不能倒流,历史不可改写,任务一旦完成或失败,状态就固定下来,永远无法改变
挂起->完成
,称之为resolve
;挂起->失败
称之为reject
。任务完成时,可能有一个相关数据;任务失败时,可能有一个失败原因。
可以针对任务进行后续处理,针对完成状态的后续处理称之为onFulfilled,针对失败的后续处理称之为onRejected
Promise API
ES6提供了一套API,实现了Promise A+规范
基本使用如下:
// 创建一个任务对象,该任务立即进入 pending 状态
const pro = new Promise((resolve, reject) => {
// 任务的具体执行流程,该函数会立即被执行
// 意思是这里的代码是同步代码
// 调用 resolve(data),可将任务变为 fulfilled 状态, data 为需要传递的相关数据
// 调用 reject(reason),可将任务变为 rejected 状态,reason 为需要传递的失败原因
});
pro.then(
(data) => {
// onFulfilled 函数,当任务完成后,会自动运行该函数,data为任务完成的相关数据
},
(reason) => {
// onRejected 函数,当任务失败后,会自动运行该函数,reason为任务失败的相关原因
}
);
要特别注意一个问题,new Promise()
中直接执行的代码是同步代码,只有调用then
之后执行的代码,才是异步代码
将求职的代码改为Promise
function sendMessage(company) {
return new Promise((resolve, reject) => {
console.log(`向 ${company} 公司提交了求职信!`);
console.log("......等待回复中......")
setTimeout(function () {
if (Math.random() >= 0.1) {
if (Math.random() >= 0.5) {
resolve({
pass: true,
msg: `恭喜你,通过了${company}公司的面试`
})
}
else {
resolve({
pass: false,
msg: `对不起,${company}拒绝了你的面试申请`
})
}
}
else {
if (Math.random() >= 0.5) {
reject({
code: 1,
msg: "你的网络出错,发不了求职信了"
})
}
else {
reject({
code: 2,
msg: "你的征信有问题,直接被拉黑"
})
}
}
}, 1000);
})
}
简单调用:
let pro = sendMessage("华为");
pro.then((data) => {
if (data.pass) {
console.log(data.msg);
}
else {
console.log(data.msg);
}
}, (reason) => {
console.log(reason.msg);
})
ajax封装为Promise
原生js封装
const ajaxPromise = param => {
return new Promise((resovle, reject) => {
var xhr = new XMLHttpRequest();
xhr.open(param.type || "get", param.url, true);
xhr.send(param.data || null);
xhr.onreadystatechange = () => {
if(xhr.readyState === 4){
if(xhr.status === 200){
resovle(JSON.parse(xhr.responseText));
} else{
reject(JSON.parse(xhr.responseText));
}
}
}
})
}
JQuery封装
const ajaxPromise = param => {
return new Promise((resolve,reject)=>{
$.ajax({
"url":param.url,
"type":param.type || "get",
"async":param.async || true,
"data":param.data || "",
dataType:param.data || "json",
"success": res => {
resolve(res);
},
"error": err => {
reject(err);
}
})
})
}
调用
ajaxPromise({
url:"/users",
}).then(data=>{
setHTML(data);
})
Comments