- Promise 是ES6中新增的一个类,专门用来解决异步回调地狱的问题,将异步代码同步显示出来
- promise 三个状态: pending 进行中 fulfilled 成功 rejected 失败
- 改变状态有两种可能 pending–>fulfilled pending–> rejected
let p = new Promise(function (resolve, reject) {
resolve(); // 调用resolve代表执then中的第一个回调,resolve代表的是成功态
// 调用reject ,就代表执行then的的第二个回调;,失败态
reject();
}).then(function () {
console.log(1);
}, function () {
console.log(2);
}).then(function () {
}, function () {
})
setTimeout(function(){
console.log("买菜");
setTimeout(function(){
console.log("洗菜");
setTimeout(function(){
console.log("做菜");
},2000);
},2000);
},3000)
let p = new Promise(function(resolve,reject){
setTimeout(function(){
console.log("买菜");
resolve();
},3000)
}).then(function(){
return new Promise(function(resolve,reject){
setTimeout(function(){
console.log("洗菜");
resolve()
},2000)
})
}).then(function(){
return new Promise(function(resolve,reject){
setTimeout(function(){
console.log("做菜");
resolve()
},2000)
})
})
<body>
<h2>需求:promise执行买菜做饭过程</h2>
<br>
1.买菜
<br>
2.洗菜
<br>
3.做饭
<br>
4.吃饭
<br>
</body>
<script>
let p = new Promise(function (resolve, reject) {
setTimeout(function () {
if (Math.random() > 0.5) {
resolve(100);
} else {
reject();
}
}, 1000)
})
// then : 在Promise的原型上
// p.then 也会默认返回一个promise实例,而且默认是成功的;
如果在then的回调函数中也返回一个promise实例,那么这个promise实例就会影响下一个then中的回调函数;
p.then(function (a) {
console.log(a);
return new Promise(function (resolve, reject) {
resolve();
});
}, function () {
console.log("不买");
}).then(function () {
// 第二个then的方法受第一个then返回值影响;
console.log("成功")
}, function () {
console.log("失败")
})
</script>
function getJSON(url) {
let p = new Promise(function (resolve,reject) {
let xhr = new XMLHttpRequest;
xhr.open('get',url,true);
xhr.responseType = 'json';
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && /^2\d{2}$/.test(xhr.status)) {
resolve(JSON.parse(xhr.responseText));
}
}
xhr.send();
})
return p;
}
getJSON('data.json').then(function (data) {
//受上面异步请求的时间控制;当异步请求成功以后,执行这个回调函数
return getJSON('1.txt')
},function () {
}).then(function () {
//这个then受第一个then中返回的新的promise实例的影响
})
- 一般来说,不要在then中定义第二个reject函数;一般使用catch;理由是第二种写法可以捕获前面then方法执行中的错误,也更接近同步的写法(try/catch)。
- 如果在then中抛出错误,就会执行下面的catch
- 如果then中有一个异常,也会抛出错误
- 只要then链中有一个异常,就会执行最后的catch;只要在then中有失败的回调,就不再执行最后的catch了
- 只要其中一个then的回调失败,就不再执行下面的then中的回调,而是直接走catch
let p = new Promise(function (resolve,reject) {
resolve();
})
p.then(function () {
console.log(1);
console.log(a);
}).then(function () {
}).then(function () {
}).catch(function () {
console.log(2);
})
//显示 1 2
- promise 的finally 是不管成功还是失败,都会执行的回调
- 这是会等到promise实例状态改变以后会触发的回调,是一个异步的;
let p = new Promise(function(resolve,reject){
resolve();
})
p.then(function(){
console.log(1);
console.log(a);
}).catch(function(){
console.log(55);
}).finally(function(){
// 这是会等到promise实例状态改变以后会触发的回调,是一个异步的;
console.log(1000);
})
console.log(999);
- Promise.all : 用于多个实例,返回一个新promise实例
- 全部成功状态才是成功,只要有一个失败状态就是失败
- 如果不是 Promise 实例,就会先调用下面讲到的
Promise.resolve()
方法,将参数转为 Promise 实例,再进一步处理。
let p1 = new Promise(function(resolve,reject){
setTimeout(function(){
reject(100);
},3000)
})
let p2= new Promise(function(resolve,reject){
setTimeout(function(){
reject(200);
},2000)
})
let p3= new Promise(function(resolve,reject){
setTimeout(function(){
resolve(300);
},4000)
});
let p = Promise.all([p1,p2,p3]) //全部执行完才会返回结果
p.then(function(data){
// 会将所有成功的数据组装一个新数组,传递给这个回调函数
console.log(data);
}).catch(function(data){
// 只要遇到一个失败,就将失败的这个值传递给这个回调
console.log(data);
})
- Promise.race()` 方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。
Promise.race()
方法的参数与Promise.all()
方法一样,如果不是 Promise 实例,就会先调用下面讲到的Promise.resolve()
方法,将参数转为 Promise 实例,再进一步处理。
let p1 = new Promise(function(resolve,reject){
setTimeout(function(){
reject(100);
},13000)
})
let p2= new Promise(function(resolve,reject){
setTimeout(function(){
reject(200);
},12000)
})
let p3= new Promise(function(resolve,reject){
setTimeout(function(){
resolve(300);
},4000)
});
let p = Promise.race([p1,p2,p3])
p.then(function(){
console.log("成功");
}).catch(function(){
console.log("失败");
})
//谁先执行返回的结果是啥p的结果就是啥
上面代码中,只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。
那个率先改变的 Promise 实例的返回值,就传递给p的回调函数。
- Promise.resolve: 将一个对象转成一个promise的实例;返回值可以调用then方法
Promise.resolve("foo").then(function(){
})
new Promise(function(resolve,reject){
resolve("foo")
});
let p = Promise.resolve();
p.then(function(){
})
let a = {
then:function(){
}
}
<script>
// 重写Promise;写一个类似于Promise这个类的方法
class MyPromise {
constructor(excutor) { // 当new MyPromise,constructor执行的;
// this --> Promise的实例;
// pending fulfilled rejected
this.state = "pending";
// 用来存储成功的回调函数和失败的回调函数的;
this.fulfilledEvent = [];
this.rejectedEvent = [];
// 1.resolve,改变了状态
let resolve = (result) => {
// 执行resolve,那么实例的状态变成了成功态;this-->Promise实例
// 如果不是pending状态,就不再向下执行;
if (this.state !== "pending") return;
this.state = "fulfilled";
clearTimeout(this.timer);
this.timer = setTimeout(() => {
this.fulfilledEvent.forEach(item => {
if (typeof item == "function") {
item(result);
}
})
})
};
let reject = (result) => {
if (this.state !== "pending") return;
this.state = "rejected";
clearTimeout(this.timer);
this.timer = setTimeout(() => {
this.rejectedEvent.forEach(item => {
if (typeof item == "function") {
item(result);
}
})
})
}
try {
excutor(resolve, reject);
} catch (e) {
// e: 错误信息
reject(e);
}
}
// 是订阅;是在往成功的事件池和失败的事件池放入成功的回调函数和失败的回调函数
then(resolveFn, rejectedFn) {
// 当then不传回调时,给两个形参赋默认值
if (resolveFn === undefined) {
resolveFn = () => {}
}
if (rejectedFn === undefined) {
rejectedFn = () => {}
}
// this
return new MyPromise((resolve, reject) => { // then 返回的这个实例是p2;
// 往事件池中放入方法;
// resolve :这个是函数,函数中的this--> p2;
// this--> then返回的promise实例
// this.fulfilledEvent.push(resolveFn);
this.fulfilledEvent.push((result) => {
try {
let x = resolveFn(result);
// resolve : 让成功的事件池中的方法运行,里面执行时,
this-->P2的事件池中的方法执行
x instanceof MyPromise ? x.then(resolve, reject) : resolve();
} catch (e) {
reject(e);
}
});
this.rejectedEvent.push((result) => {
try {
let x = rejectedFn(result);
// resolve : 让成功的事件池中的方法运行,里面执行时,
this-->P2的事件池中的方法执行
x instanceof MyPromise ? x.then(resolve, reject) : resolve();
} catch (e) {
reject(e);
}
});
})
}
}
let p1 = new MyPromise(function (resolve, reject) {
// resolve(100)
// resolve(200);
// reject();
// console.log(a);
// console.log(resolve);
resolve();
})
p1.then(function (a) {
// fn1
// 成功的回调函数
console.log(a);
return new Promise(function (resolve, reject) { //:x===p3
resolve
(); // 这个resolve 执行,能让p3的事件池中的方法执行,
p3的事件池中有个resolve;所以就会这个p3事件中的resolve执行,p3中的resolve执行,
这个this指向p2,那么就能让p2事件池中的方法运行;
})
}, function () {
// fn2
// 失败的回调
console.log(88);
}).then(function () {
// fn3
// 如果上一个then中的回调返回一个promise实例,那么这个函数会被resolve包裹一下,
再放进这个实例的事件池中
}, function () {
})
//console.log(p1);
// p1 p2 p3
// 第一个then:把方法放到p1的事件池中;
// 第二个then:把方法放到p2的事件池中;
//
let p = new Promise(function (resolve, reject) { // resolve : 形参
// 如果代码异常,会走失败;不会在控制台抛出异常
//console.log(a);
resolve();
})
let c = p.then(function () {
//
}).then(function () {
// 上一个中的回调如果不返回promise实例,
那么这个then受上一个then默认返回的promise的状态影响,如果回调中返回了promise实例,
那么这个then就受返回的promise实例的影响
}, function () {
});
console.log(c);
</script>
<script>
class MyPromise{
constructor(excutor){
this.state = "pending";// 当前实例有个state,默认值是pending;
// 当初始化promise实例时,新增两个事件池;一个成功一个失败;
this.fulfilledEvent = [];
this.rejectedEvent=[];
// resolve : 1. 改变状态;2.让成功事件池中的方法都执行;
let resolve=(result)=>{
// 如果不是pending状态,那么promise状态已经发生了改变,不需要再执行;
if(this.state!=="pending")return;
this.state="fulfilled";
clearTimeout(this.timer);
this.timer = setTimeout(()=>{
// 事件池中的方法执行是一个异步的;
this.fulfilledEvent.forEach(item=>{
if(typeof item==="function"){
item(result)
}
})
},0)
};
let reject = (result)=>{
if(this.state!=="pending")return;
this.state="rejected";
clearTimeout(this.timer);
this.timer = setTimeout(()=>{
this.rejectedEvent.forEach(item=>{
if(typeof item==="function"){
item(result)
}
})
},0)
};
try{
excutor(resolve,reject)
}catch(e){
reject(e);
}
}
// 订阅方法;
then(resolveFn,rejectFn){
//如果then不传参数;给函数赋默认的匿名函数;
if(resolveFn===undefined){
resolveFn=()=>{};
}
if(rejectFn===undefined){
rejectFn=()=>{
throw new Error();
}
}
return new MyPromise((resolve,reject)=>{// p1 p1中的resolve--> this指向p1;
// 必须验证resolveFn是否是一个promise的实例
this.fulfilledEvent.push((result)=>{
try{
let x = resolveFn(result);
// x===p2 resolve 存储到了p2的事件池中;
x instanceof MyPromise?x.then(resolve,reject):resolve();
}catch(e){
reject(e);
}
});
this.rejectedEvent.push((result)=>{
try{
let x = rejectFn(result);
// x===p2 resolve 存储到了p2的事件池中;
x instanceof MyPromise?x.then(resolve,reject):resolve();
}catch(e){
reject(e)
}
});
})
}
}
let p = new MyPromise(function (resolve,reject) {
setTimeout(function () {
resolve(1)
},2000)
})
p.then(function (val) {
/*return new MyPromise(function (resolve,reject) {
setTimeout(function () {
resolve(2);
},2000)
})*/
},function () {
}).then(
function () {
}
)
/*let p = new MyPromise(function (resolve,reject) {
// resolve形参可以执行;所以该实参一定是个函数
// resolve : 改变当前实例的状态
resolve()
});
p.then(function () {
return new MyPromise(function (resolve,reject) { // p2
resolve();// 执行p2事件池中的方法;p2事件池中有个p1 的resolve,p1的resolve执行;
会把p1成功事件池中的方法执行;
})
}).then(function () {
});*/
// 第一个resolve 要执行p这个实例中成功事件池中的方法;这是一个异步的操作;
暂时异步事件池中的方法是不执行;
// 第二个resolve : 执行p2事件池中的方法;p2事件池中有个p1 的resolve,p1的resolve执行;
会把p1成功事件池中的方法执行;
// 第一个then : 把then中的方法放入到p的成功事件池中;并且返回一个p1的promise实例;
// 第二个then :p1.调用的then;把then中放到了p1.的成功事件池中;
/* let p1 = new Promise(function (resolve) {
resolve()
})
p1.then(function () {
// then : 向事件池中存储方法;
})
p1.then(function () {
})
console.log(p1);*/
/*class A{
constructor(num){
// this--> 实例
this.num = num;
}
// 原型上的方法
then(){
console.log(this);
}
}
let a = new A(100);
a.then()*/
</script>
ES6 语法规范中新加的内置类,用来处理 js 中异步编程的,而我们所谓的 Promise 设计模式,就是基于 promise 对异步操作进行管理
promose 是一个内置类,所以创建一个 promise:new Promise([executor]): 第一个执行函数必须传递,这里的 executor 是一个回调函数下面简称 exe
new promise 的时候就会把 exe 执行,创建 promise 的一个实例(exe 是 promise 类的一个回调函数,promise 内部会把它执行)
promise 不仅把 exe 执行,而且还给 exe 传递两个参数(两个参数也是函数类型)
resolve 函数:它执行代表 promise 处理的异步事情是成功的,把 promise 的状态改为 fulfilled
reject 函数:它执行代表 promise 处理的异步事情是失败的,把 promise 的状态改为 rejected
exe 函数中放的就是当前要处理的异步操作事情
let promiseExamp = new Promise((resolve, reject) => {这里一般存放的都是我们即将要处理的异步任务,任务成功我们执行 resolve,任务失败我们执行 reject(当然写同步的也可以)}
举个例子:
let ran = Math.random();
setTimeout(() => {
if (ran < 0.5) {
reject(ran);
return;
}
resolve(ran);
}, 1000);
promiseExamp.then(result => {
// 状态为 FULFILLED 成功后执行(RESULT:[[PromiseValue]])
console.log('成功: ' + result);
}, error => {
// 状态为REJECTED 失败后执行
console.log('失败: ' + error);
});
一个回调函数嵌一个回调函数再嵌一个回调函数再嵌一个回调函数再嵌一个回调函数再嵌一个回调函数再嵌一个回调函数再嵌一个回调函数再嵌一个回调函数再嵌一个回调函数再嵌一个回调函数再嵌一个回调函数再嵌一个回调函数再嵌一个回调函数再嵌一个回调函数再嵌一个回调函数再嵌一个回调函数再嵌一个回调函数再嵌一个回调函数再嵌一个回调函数再嵌一个回调函数再嵌一个回调函数再嵌一个回调函数再嵌一个回调函数再嵌一个回调函数再嵌一个回调函数… 就是回调地狱。
一个小例子:
从服务器获取某个学生的基本信息 (score-id) -> 基于分数的 ID 获取到当前学生各项成绩 -> 基于某一项成绩获取他在学校的排名
只有第一个请求成功才能执行第二个,第二个成功才能执行第三个,最后一个请求成功后拿到了每一次请求的所有数据。
$.ajax({
url:'/baseInfo',
method:'GET',
data:{
name:'zhanglu'
},
success:result => {
let scoreId = result.scoreId;
$.ajax({
url:'/scoreInfo',
method:'GET',
data:{
id:scoreId
},
success:result => {
let chinese = result.chinese;
$.ajax({
url:'/paiming',
method:'GET',
data:{
num:chinese
},
success:result => {
}
});
}
});
}
});
三个请求可以同时发送,但是需要等到所有请求都成功才会做一件事
let chi = 100,
eng = 12,
math = 98;
let chiPai,
engPai,
mathPai;
let count = 0;
function func () {
if(count >= 3) {
// 处理自己要做的事情
}
}
$.ajax({
url:'/pai ? chi = ' + chi,
success:result => {
chiPai = result;
count++;
func();
}
});
$.ajax({
url:'/pai ? eng = ' + eng,
success:result => {
engPai = result;
count++;
func();
}
});
$.ajax({
url:'/pai ? math = ' + math,
success:result => {
mathPai = result;
count++;
func();
}
});
function queryBase () {
return new Promise (resolve => {
$.ajax({
url: '/baseInfo?name=zhanglu',
success: result => {
resolve(result);
}
});
});
}
function queryScore (scoreId) {
return new Promise(resolve => {
$.ajax({
url: '/score?id=' + scoreId,
success: result => {
resolve(result);
}
});
});
}
function queryChinese (chinese) {
return new Promise (resolve => {
$.ajax({
url: '/paiming?chin=' + chinese,
success: result => {
resolve(result);
}
});
});
}
function ajax1 () {
return new Promise (resolve => {
$.ajax({
url: '/api1',
success: resolve
});
});
}
function ajax2 () {
return new Promise (resolve => {
$.ajax({
url: '/api2',
success: resolve
});
});
}
function ajax3 () {
return new Promise (resolve => {
$.ajax({
url: '/api3',
success: resolve
});
});
}
Promise.prototype 上面有 then 的方法
pro.then ([success],[error])
pro.then ([success],null)
pro.then (null,[error])
catch: 设置失败后执行的方法
finally: 设置不论成功还是失败都会执行的方法(一般不用)
new Promise((resolve, reject) => {
// resolve(100); // 把第一个 promise 实例的 value 值改为100 / -100
reject(-100);
}).then(result => {
console.log(result);
return result * 10; // then 中 return 的结果相当于把当前这个新的 promise 实例中的 value 值
// 改为返回值
}, err => {
console.log(err);
return err / 10;
}).then(A => {
console.log('A:' + A);
}, B => {
console.log('B:' + B);
}).then(C => {
}, D => {
});
执行 then / catch / finally 返回的结果是一个全新的 promise 实例,所以可以链式写下去;下一个 then 中哪个方式会被执行,由上一个 then 中某个方法执行的结果来决定
上一个 then 中某个方法的返回值会传递给下一个 then 的某个方法中
如果当前 promise 实例的状态确定后,都会到对应的 then 中找方法,如果 then 中没有对应的这个方法,则会向下顺延
then 方法中如果返回的是一个 promise 实例,则当前返回实例的成功或者失败状态,影响着下一个 then 中哪个方法会被触发执行;如果返回的是非 promise 实例,则看当前方法执行是否报错,来决定下一个 then 中哪个方法执行;
new Promise((resolve, reject) => {
resolve();
}).then().catch(x => {
console.log(1);
}).then(x => {
console.log(2); // OK
}).then(x => {
console.log(3); // OK
}).catch(x => {
console.log(4);
}).then(x => {
console.log('AAA'); // OK
console.log(AAA); // 报错
}).catch().then(null, x => {
console.log(5); // OK
});
Promise.all([promise1, promise2,…]):all 中存放的是多个 promise 实例(每一个实例管理者一个异步操作),执行 all 方法返回的结果是一个新的 promise 实例 “PROA”
当所有 promise 实例的状态都为 Fulfilled 的时候(成功),让 PROA 的状态也变为 Fulfilled,并且把所有 promise 成功获取的结果,存储为成为一个数组(顺序和最开始编写的顺序一致)“result=[result1,result2,…]”,让 PROA 这个数组的 value 值等于这个数组
都成功(PROA 状态是 fulfilled)才会通知 then 中第一个方法执行,只要有一个失败(PROA 状态是 recected),就会通知 then 中第二个方法或者 catch 中的方法执行
Promise.all([ajax1(), ajax3(), ajax2()]).then(results => {
// results:[result1, result3, result2]
});
Promise.race([ajax1(), ajax3(), ajax2()]).then(result => {
// 看哪一个 promise 状态最先处理完(成功或者失败),以最先处理完的为主
});