promise构造函数
基本使用
Promise构造函数接收一个函数,这个函数是同步执行的,
当这个Promise构造函数被调用时,传入的函数中的代码
是一个同步代码,而不是js的异步任务。
这个函数接收两个参数(他们两都是函数)
const p = new Promise((resolve, reject) => {
console.log("执行1");
resolve("成功");
});
console.log("执行2");
console.log(p);
Promise的工作流程
我们发现"执行1","执行2"按照正常的书写顺序输出了。说明Promise构造函数的参数函数中
代码是同步代码。
Promise的状态
pending(进行中)
fulfilled(已成功)
rejected(已失败)
Promise.resolve()
resolve()传入一个promise对象,那么返回值是一个promise对象,状态和值由
“传入的promise对象”决定。
传入一个非promise对象,返回值是一个promise对象,状态永远为已完成,
值为传入的值。
const p1 = Promise.resolve(521);
const p2 = Promise.resolve(new Promise((resolve,reject)=>{
resolve('成功');
}));
// 传入一个错误,p3的状态还是成功
const p3 = Promise.resolve(new Error('错误'));
const p4 = Promise.resolve(new Promise((resolve,reject)=>{
reject('失败');
}))
console.log(p1);
console.log(p2);
console.log(p3);
console.log(p4);
Promise.reject()
reject()传入一个promise对象,那么返回值是一个promise对象,
状态永远为失败,值是传入的promise对象
传入一个非promise对象,返回值是一个失败的promise对象,
状态永远为失败,值为传入的值
const p1 = Promise.reject(521);
const p2 = Promise.reject(new Promise((resolve,reject)=>{
resolve('成功');
}))
const p3 = Promise.reject(new Promise((resolve,reject)=> {
reject('失败');
}))
console.log(p1);
console.log(p2);
console.log(p3);
Promise.all()
接收多个promise对象组成的数组。
只有当所有的promise对象的状态都为已完成,返回的promise对象的状态才为已完成。
当状态为已完成时,值为各个promise对象的值组成的数组。
当状态为已失败时,值为第一个失败的promise对象的值。
const p = Promise.all([
new Promise((resolve, reject) => {
resolve("成功1");
}),
new Promise((resolve, reject) => {
resolve("成功2");
}),
new Promise((resolve, reject) => {
reject("失败1");
}),
new Promise((resolve,reject)=>{
reject("失败2")
})
]);
console.log(p);
Promise.race()
race(竞速的意思)顾名思义:接收promise对象组成的数组,值和状态由第一个返回值和状态的promise对象决定。
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
console.log("成功1");
resolve("成功1");
}, 2000);
});
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
console.log("失败");
reject("err");
}, 1000);
});
let p = Promise.race([p1, p2]);
setTimeout(() => {
console.log(p);
}, 1000);
Promise.prototype.then/catch()/finally()
const p = new Promise((resolve,reject) => {
// resolve('成功')
reject('失败')
}).then((value)=>{
console.log(value); // '成功'
}).catch((err)=>{
console.log(err); // '失败'
}).finally(()=>{
// 不管promise最后的状态,在执行完then或catch指定的回调函数以后,都会执行finally方法指定的回调函数
console.log("不管promise的状态,我永远会输出");
})
es11新增Promise.allSettled()
const p1 = new Promise(function(resolve, reject) {
setTimeout(function() {
resolve('p1成功');
}, 1000)
})
const p2 = new Promise(function(resolve, reject) {
setTimeout(function() {
reject('p2失败');
}, 1000)
})
// 返回一个promise对象,状态属性永远是fulfilled完成,值属性的值是一个数组,数组每一项是每个promise对象的状态和值对象
const result1 = Promise.allSettled([p1, p2]);
// 相似方法all,也是返回一个promise对象,状态由传入的promise对象决定,都为完成时,返回的promise对象的状态才为完成,值为一个数组,数组每一项是每个promise对象的值。有失败的话,状态为失败,值为第一个失败的promise的值。
const result2 = Promise.all([p1,p2]);
console.log(result1);
console.log(result2);
Promise的注意事项
Promise.then()方法的返回值由谁决定
then方法可以只写第一个参数,也就是状态为已完成时调用的函数
then()方法返回一个promise对象,这样就可以进行链式调用了,解决回调地狱
const p = new Promise(function(resolve,reject){
setTimeout(function(){
let data = '用户数据';
resolve(data);
},1000)
})
const result = p.then(function(value){
// 1、默认return 返回一个 undefined,状态为已完成,值为undefined
console.log(value);
// 2、return的值不是一个promise对象时,状态为已完成,值为return的值
// return 123
// 3、throw抛出一个结果,状态为拒绝,值为抛出的值
// throw '123'
// 4、抛出一个error错误对象,状态为拒绝,值为抛出的error对象
// throw new Error('123');
// 5、return一个promise对象,状态为下一级promise对象的状态,值也是下一级promise的值
return new Promise(function(resolve,reject){
// promise封装异步操作,也可以执行同步代码
// resolve('123');
reject('123');
})
})
console.log(result);
promise怎么串联调用多个同步异步任务
let p1 = new Promise((resolve, reject) => {
resolve("p1成功");
});
let p2 = p1.then((value) => {
return new Promise((resolve, reject) => {
console.log(value); // p1成功
resolve("p2成功");
})
.then((value) => {
console.log(value); // p2成功
// return {PromiseState:fulfilled,PromiseResult:undefined}
})
.then((value) => {
console.log(value); //undefined,因为上一个then默认return了一个{PromiseState:fulfilled,PromiseResult:undefined}
});
});
promise的异常穿透
在then链式调用的时候在最后指定错误的回调,不管是链式调用的哪个环节失败了,
都只需要在最后指定失败的回调即可
let p1 = new Promise((resolve, reject) => {
resolve("成功");
})
.then(value => {
return new Promise((resolve, reject) => {
reject("err1");
});
})
// 因为上一个then已失败,故不会继续执行后面的then。
.then(value => {
return new Promise((resolve, reject) => {
reject("err2");
});
})
.catch(err => {
console.log(err); // err1
});
一个promise怎么指定多个成功或者失败的回调函数
const p = new Promise((resolve, reject) => {
resolve("ok");
});
p.then((value) => {
console.log(value);
});
p.then((value) => {
console.log(value + "2");
});
先改变状态还是先执行回调
当执行器中是同步任务的时候,先改变状态后执行回调
const p = new Promise((resolve, reject) => {
resolve("OK");
});
p.then(
value => {
console.log(value);
},
reason => {
console.log(reason);
}
);
console.log(p);
当执行器中是异步任务的时候,先执行回调后改变状态。
当p.then()需要异步操作更长的时间的,先改变状态后执行回调。
const p = new Promise((resolve, reject) => {
// 当执行器中是异步任务的时候,先执行回调后改变状态。
setTimeout(()=>{
resolve("OK");
},1000)
});
p.then(
value => {
console.log(value);
},
reason => {
console.log(reason);
}
);
setInterval(()=>{
console.log(p);
},10)
可以改变promise状态的方法
1、resolve
2、reject
3、throw
const p = new Promise((resolve,reject)=>{
// 1.resolve 成功
// resolve('OK');
// 2.reject 失败
// reject('err');
// 3.throw 失败
throw 123;
})
console.log(p);
如何中断链式调用链
打断链式调用的唯一方式,返回一个pedding状态的promise对象,也就是未有修改promise
状态代码的Promise对象
let p = new Promise((resolve, reject) => {
resolve('成功')
}).then(value=>{
console.log(value); // 成功
}).then(value=>{
console.log(value); // undefined,前一个then默认返回
// 在这。。。
return new Promise((resolve,reject)=>{}) // 在这打断
}).then(value=>{
console.log(value); // 前面被打断了,不会被调用
console.log('调用到我这来了吗?'); // 前面被打断了,不会被调用
})
promise会开启一个微任务
具体解析可以看js专栏的中宏任务与微任务,并为大家讲解一道面试题帮助大家理解。
Promise的使用场景
promise链式调用解决回调地狱
const fs = require('fs');
// 回调地狱,读取文件,获取读取数据库中的数据有先后顺序时
// fs.readFile('./files/文件1.md',function(err1,data1){
// fs.readFile('./files/文件2.md',function(err2,data2){
// fs.readFile('./files/文件3.md',function(err3,data3){
// let result = data1 + data2 + data3;
// console.log(result);
// })
// })
// })
// 使用promise封装
const p = new Promise(function(resolve,reject){
fs.readFile('./files/文件1.md',function(err,data){
resolve(data);
})
}).then(function(value){
return new Promise(function(resolve,reject){
fs.readFile('./files/文件2.md',function(err,data){
resolve([value,data]);
})
})
}).then(function(value){
return new Promise(function(resolve,reject){
fs.readFile('./files/文件3.md',function(err,data){
value.push(data);
let result = value.join(';');
console.log(result);
})
})
})
使用promise封装读取文件
const fs = require('fs');
// 不使用promise封装
// fs.readFile('./files/文件1.md',function(err,data){
// if(err) throw err;
// // buffer转字符
// console.log(data.toString());
// })
// 使用promise封装
const p = new Promise(function(resolve,reject){
fs.readFile('./files/文件1.md',function(err,data){
// reject和resolve方法都有终止代码向下执行的功能
if(err) reject(err);
resolve(data)
})
})
p.then(function(value){
console.log(value.toString());
},function(reason){
console.log(reason);
})
使用promise封装ajax请求
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<script type="text/javascript">
function a() {
return new Promise(function(resolve, reject) {
const xhr = new XMLHttpRequest();
xhr.open("GET", "https://api.apiopen.top/getJoke");
xhr.send();
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
if (xhr.status >= 200 && xhr.status < 300) {
resolve(xhr.response);
console.log(xhr.response);
} else {
reject(xhr.status);
}
}
};
});
}
a().then(
function(value) {
console.log(value);
},
function(reason) {
console.error(reason);
}
);
</script>
</body>
</html>
axios
前端与后端通信最常见的axios也是基于promise封装的。
有时间也是准备为大家更新博客怎么手写promise和axios。