这篇文章适合要接触Promise的朋友们阅读,里面写的是Promise中的一些基础知识,并没有涉及到太难理解的。
一、 初识Promise
1.1 什么是Promise
Promise:ES6中新提供的一个内置类(提到类可以想到new、原型、原型链、实例...),基于promise可以有效的管理JS中的异步编程,解决传统异步编程+回调函数导致的‘回调地狱’问题。
=> 我们把基于promise进行异步管控的模式叫做‘promise设计模式’
1.2 学习Promise需要掌握的内容
从函数有三个角色入手:普通函数、构造函数(类)、普通对象
【普通对象 => 静态的私有属性方法】
- promise.all()
- promise.race()
- promise.resolve()
- promise.reject()
【类 => 原型链上的公共属性和方法】
- promise.prototype.then()
- promise.prototype.catch()
- promise.prototype.finally() [一般不用]
【new创建实例】
二、创建Promise实例
2.1 创建Promise实例的语法
语法: let 实例 = new Promise([executor])
说明:
- 必须传一个函数,否则会报错:Uncaught TypeError: Promise resolver undefined is not a function
[executor]
是一个函数,我们一般在函数中管控我们的异步编程代码new Promise
的时候就会把executor
立即执行- 并且给
executor
函数传递两个实参(两个实参也都是函数):resolve/reject
let p1 = new Promise(); //必须传一个函数 Uncaught TypeError: Promise resolver undefined is not a function
let p2 = new Promise((resolve,reject)=>{
//异步编程代码
});
2.2 Promise的两个值
Promise的实例拥有[[PromiseStatus]]/[[PromiseValue]]
[[PromiseStatus]]是promise状态(要么是成功态要么是失败态)
- 准备状态
pending
:new Promise
的时候默认状态就是pending
- 成功状态
fulfilled/resolved
:一般在异步操作成功后,我们通过执行resolved
函数,可以把promise
的状态改为resolved
- 失败状态
rejected
:一般在异步操作失败后,我们通过执行reject
函数,可以把promise
的状态改为rejected
pending
=> resolved / rejected
只要状态一旦更改,则不可以再改变
[[PromiseValue]]是promise的值
- 不论执行
resolve/reject
哪个函数,都可以传递值,传递的值最后赋值给[[PromiseValue]]
例如: 我们创建一个Promise实例p1,创建实例的时候我们在函数中把他的resolved函数执行,也就是把它变为成功态
let p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('ok');
console.log(p1) //=>'resolve' 'ok'
}, 1000)
});
console.log(p1); //=>'padding' undefined
2.3 总结:创建实例时做的事情
我们在创建一个Promise的实例,需要给Promise传递一个函数,这个函数会立即执行,并且浏览器给它自带两个参数(两个参数也都是函数),这两个参数会改变Promise的状态和值
三、Promise原型链上的公共方法---then
3.1 then是干什么的
我们在创建实例的时候可以修改Promise的状态,目的就是为了控制then中的两个方法,哪一个去执行。
then
方法:实例.then([状态成功时执行的],[状态失败时执行的])
result / reason
接收的是[[PromiseValue]]
的信息(在executor
函数中,基于resolve/reject
执行传递的值,就是给promise-value
传递的值,并且只能传递一个值,传递第二个实参没用)
let p1 = new Promise((resolve, reject) => {
setTimeout(() => {
if (Math.random() < 0.5) {
reject('NO');
} else {
resolve('OK');
}
}, 1000);
});
p1.then(result => {
console.log(`成功:${result}`);
}, reason => {
console.log(`失败:${reason}`); //当上面的随机是小于0.5的时候执行reject,也就是把状态改为失败态,从而执行then中的reason这个方法
});
3.2 then方法在什么时候执行?
1. 创建实例时执行的是异步代码:
- 异步请求放置在
EXECUTOR
中,请求成功或者失败后做啥事情都写在THEN
中
2. 创建实例时执行的不是异步代码:
EXECUTOR
函数中理论上是管控异步编程代码的,但是在开发中,你可以自己随意处理;但是不论怎么处理,THEN
中的方法,只会在PROMISE
状态变为成功或者失败的状态下才会执行;- 在
EXECUTOR
函数中执行RESOLVE
或者REJECT
,并不一定会立即通知THEN
中的方法执行;如果在这两个函数执行之前,已经基于THEN
把成功或者失败的方法放置好了,则立即通知执行;如果还没有执行过THEN
方法,则需要等到THEN
执行后,方法放置好,再通知成功或者失败的方法执行!
new Promise((resolve, reject) => {
// 异步请求放置在EXECUTOR中,请求成功或者失败后做啥事情都写在THEN中
$.ajax({
url: '/api/info',
method: 'get',
success: result => {
resolve(result);
},
error: reject
});
}).then(result => {
}, reason => {
});
new Promise((resolve, reject) => {
reject(100);
}).then(result => {
console.log(`成功:${result}`);
}, reason => {
console.log(`失败:${reason}`);
});
3.3 then方法的返回值
每一次执行.then
都会返回一个新的Promise
实例(初始状态:pending
初值:undefined
)。这样可以继续.then
下去,这就是Promise
中的then
链机制
下面的例子说明:
p2这个实例的成功或者失败状态,是由p1.then
这堆代码决定的
- 第一种情况:只要
p1.then
中不论哪个方法执行,只要不报错,新的p2实例的状态都会变为成功态,而方法返回的结果就是p2实例的promise-value
值(也就是上一个then
执行的返回结果,会传递给下一个then
中的方法);同理,两个方法中,不管哪一个执行报错,P2一定是失败态 - 第二种情况:如果
p1.then
中的某个方法执行,返回的是新的Promise
实例,则会等待这个Promise
的执行结果,作为p2的执行状态
let p1 = new Promise((resolve, reject) => {
setTimeout(() => {
if (Math.random() < 0.5) {
reject('NO');
} else {
resolve('OK');
}
}, 1000);
});
let p2 = p1.then(result=>{
},reason=>{
});
console.log(p2);
let p3 = p2.then(result => {
console.log(result); //=>'OK@@'
// return Promise.resolve(100); //=>P3的状态变为成功,值是100
return Promise.reject(0); //=>P3的状态变为失败,值是0
}, reason => {
});
p3.then(result => {
console.log('成功' + result);
}, reason => {
console.log('失败' + reason);
});
3.4 then链的理解
下面代码的输出结果:把这段代码理解会then就掌握差不多了(链有点长😂,不着急,下面搭配了注释😊)
new Promise(resolve => {
setTimeout(() => {
resolve(10); //1000MS后 第一个PROMISE实例状态是成功 VALUE:10
}, 1000);
}).then(result => {
console.log(`成功:${result}`); //=>'成功:10'
return result * n; //报错:ReferenceError: n is not defined 也就是让此次THEN返回的PROMISE实例变为失败态,VALUE:失败的原因
}, reason => {
console.log(`失败:${reason}`);
return reason * 10;
}).then(result => {
console.log(`成功:${result}`);
return result * 20;
}, reason => {
console.log(`失败:${reason}`); //=>'失败: ReferenceError: n is not defined'
return reason * 10; //NaN 代码执行没有报错,让当前THEN返回的实例状态:成功 VALUE:NAN
}).then(result => {
console.log(`成功:${result}`); //=>'成功:NaN'
return result * 20; //NaN 代码执行没有报错,让当前THEN返回的实例状态:成功 VALUE:NAN
}, reason => {
console.log(`失败:${reason}`);
return reason * 10;
}).then(result => {
console.log(`成功:${result}`); //=>'成功:NaN'
return Promise.reject(result * 20); //返回的新的失败的PROMISE实例影响了本次THEN返回PROMISE实例的状态和结果(和RETURN的保持一致)
}, reason => {
console.log(`失败:${reason}`);
return Promise.resolve(reason * 10);
}).then(result => {
console.log(`成功:${result}`);
}, reason => {
console.log(`失败:${reason}`); //=>'失败:NaN'
});
3.5 then中只写一个方法的理解
在THEN
中只设置一个方法,成功或者失败执行的方法没有设置,会顺延到下一个THEN
中查找(依然是按照当前的状态查找,例如:失败的状态,第一个THEN
没有设置失败的方法,会找第二个THEN
中设置的失败的方法...)
比如么有设置 reason
,那么需要执行它的时候默认做的是什么事情呢?
=> { 不写的情况下,PROMISE
内部默认补充了一下(实现的效果:继续找下一个THEN
中代表失败的)}
=> 即执行的是这句代码(因为是执行的失败态,所以返回的实例是失败态):return Promise.reject(reason);
说了这么多,到底理解不理解🙄,哈哈上代码🙈(内心是拒绝的,因为它肯定也会是很长的then链)
new Promise((resolve, reject) => {
setTimeout(() => {
reject(10); //状态:失败 值:10
}, 1000);
}).then(result => {
console.log(`成功:${result}`);
return result * 10;
}).then(result => {
console.log(`成功:${result}`);
return result * 10;
}).then(null, reason => {
console.log(`失败:${reason}`); //=>'失败:10'
return reason * 2;
//代码执行没有报错,让当前实例状态:成功 值:20
}).then(null,reason => {
console.log(`失败:${reason}`);
return reason * 2;
}).then(result => {
console.log(`成功:${result}`); //=>'成功:20'
});
四、Promise.all()
Promise.all([PROMISE1,PROMISE2,...])
:等待所有的PROMISE
实例都成功,整体才是成功的(返回新的PROMISE
实例),只要有一个实例是失败的,整体实例就是失败的;
function fn1() {
return new Promise(resolve => {
setTimeout(_ => {
resolve(10);
}, 2000);
});
}
function fn2() {
return Promise.resolve(20);
}
function fn3() {
return new Promise(resolve => {
setTimeout(_ => {
resolve(30);
}, 500);
});
}
Promise.all([fn1(), fn2(), fn3()]).then(results => {
// 只有当三个PROMISE实例都成功的时候(等待最晚有结果的一个也是成功),才会触发执行,results会按照放置的顺序,存储着每一次获取的结果
console.log(results); //=>[10, 20, 30]
});
五、Promise.race()
Promise.race()
同时发送多个请求,谁先有处理结果(不管结果是成功还是失败),就以谁的结果为主(哪怕是失败的)
六、【补充】:异常捕获
6.1 catch表示then 中只写一个reason方法
在项目中,我们会用 CATCH(REASON=>{})
代替 THEN(NULL,REASON=>{})
,效果是一模一样的(执行CATCH
也会返回新的PROMISE
实例,里面设置的方法是在实例为失败状态下执行的)
=>在项目中,我们一般THEN
中放的是成功执行的,CATCH
中放的是失败执行的
new Promise((resolve, reject) => {
setTimeout(() => {
reject(10);
}, 1000);
}).then(result => {
console.log(`成功:${result}`);
return result * 10;
}).catch(reason => {
console.log(`失败:${reason}`);
return reason * 2;
});
6.2 异常捕获:try catch
为什么要用try catch
?
比如下面的代码:我们输出一个从来没有定义过的变量,会报错,下面的代码也不再执行,但是我们想让下面的代码继续执行,就需要用到try catch
console.log(n); //=>Uncaught ReferenceError: n is not defined 浏览器抛出异常信息,下面代码就不会再执行了
console.log('OK');
try catch
try
:把可能会报错的代码放置到try
中捕获异常(代码执行一但报错,控制台是不抛出异常的,不会影响后续代码的执行)catch
:catch
中捕获到异常信息 (可以把信息上报给服务器)
try {
// 把可能会报错的代码放置到TRY中捕获异常(代码执行一但报错,控制台是不抛出异常的,不会影响后续代码的执行)
console.log(n);
} catch (error) {
// CATCH中捕获到异常信息 (可以把信息上报给服务器)
// console.log(error);
}
console.log('OK');