目录
Promise是JavaScript的ES6标准新增功能,主要用来执行异步任务。
在JS中,所有代码都是单线程执行的。但是有很多事件,比如网络请求,必须是异步执行。
一个引例:执行一个任务task
//先定义2个函数
function resolve(s){
console.log('成功:'+s);
};
function reject(s){
console.log('失败:'+s);
};
//定义任务函数,需要2个参数,这2个参数也是函数
function task(resolve, reject) {
//do task...
//定时器,下面的内容2s后才执行
setTimeout(()=>{
if (true) {
resolve('i am resolve');
}
else {
reject('i am reject');
}
},2000);
console.log('任务完毕');
}
//执行任务
task(resolve,reject);
输出结果:
任务完毕
成功:i am resolve
本例中,要执行任务函数task,需要2个参数:resolve,reject
resolve和reject这两个参数也是函数,通常是处理一下任务的返回结果。
任务执行后,如果任务成功了,我们让它执行resolve,如果失败了,我们让它执行reject。
本例中可以看到,task任务执行完毕了,然后才执行了resolve。
这样,任务函数task只管执行任务,不必理会任务执行完了要干什么;
resolve和reject函数只管处理任务的结果,不必理会任务是怎么执行的,什么时候结束的。
这个过程就类似网络请求的过程,网络请求只管去拿数据,但是要花一些时间,请求成功了一般就返回请求的数据,请求失败了就返回错误信息。那么只要成功了,就走resolve函数,并且把请求数据也传过去了(相当于 'i am resolve'),如果失败了,就走reject函数,也把错误信息传过去了(相当于 'i am reject')。
Promise的基本结构
new Promise(function).then(func1).catch(func2);
Promise是个类,需要传1个参数,参数就是它的构造函数(一般是异步函数),
这个异步函数又需要2个参数resolve、reject,这两个也都是函数,
其实这个Promise参数就是要执行的任务函数,在创建Promise对象时,会调用构造函数,开始执行任务。
new Promise(function(resolve, reject){
//执行该任务...
if(任务成功){
resolve(res);//这个函数的定义就在then里面
}
else{
reject(err);//这个函数的定义就在catch里面
}
}).then(res=>{
//任务成功,do something...
}).catch(err=>{
//任务失败,do something...
});
promise对象有2个方法:then、catch
then方法的实参,就是resolve函数定义;执行then,就是执行resolve,参数res就是任务成功后的返回结果,通常就是请求的数据。
catch方法的实参,就是reject函数;执行catch,就是执行reject,参数err就是任务失败后的返回结果,通常就是错误信息。
现在,我们用Promise来改造前面的引例:
new Promise(function task(resolve, reject){
//do task...
setTimeout(()=>{
if (true) {
resolve('i am resolve');
}
else {
reject('i am reject');
}
},2000);
console.log('任务完毕');
}).then(res=>{
console.log('成功:'+res);//任务成功,走这里
}).catch(err=>{
console.log('失败:'+err);//任务失败,走这里
});
注:
1. 任务函数通常是异步函数,需要一定时间。在上述例子中,使用setTimeout定时器的目的是模拟异步函数的执行时间,可以看到执行任务和处理结果是无关的。
2. 如果任务函数是同步函数(按顺序执行),那么使用Promise就没有意义了,直接使用普通函数去执行任务就行了。
Promise对象的链式调用
Promise的通用使用方式本身就是链式调用:
new Promise(function).then(func1).catch(func2);
其中,then和catch里面传递的都是函数。
then和catch里还可以传递其他的promise对象,当然,promise对象又有then和catch方法,又可以继续调用下去,形成链式调用。
//新的任务:累加,返回Promise对象
function add(n) {
return new Promise(function (resolve, reject) {
resolve(n + n);
});
}
//把Promise对象封装到函数里
function grow(n){
return new Promise(function (resolve, reject) {
resolve(n);
});
}
//连续执行任务
grow(2).then(add).then(add).then(add).then(res=>{
console.log(res);
});
以上代码输出结果:16
解读:
1.上述grow函数、add函数,都返回promise对象,把它封装成函数的目的是为了传参数。
2.grow函数的promise任务,返回2,传给了then;第1个add任务接收到2,返回4,传给了第2个add,第2个add返回8,传给第3个add,最后第3个add返回16,传给了最后的then,所以最终的计算结果是:16。
因此,我们可以向下面这样使用Promise链式调用:
job1.then(job2).then(job3).then(handleResult).catch(handleError);
注意:job1、job2、job3,都是Promise对象。
如果中间某个任务失败,会直接执行这个任务的catch,后面的任务不会执行了。
async异步函数同步化
Promise对象执行的任务都是异步函数,返回值通过then方法的res参数接收。
我们再看下面的例子:
定义一个异步函数grow,来执行异步任务add,add是promise对象。
//累加任务
function add(n) {
return new Promise(function (resolve, reject) {
resolve(n + n);
});
}
//定义异步函数
async function grow(n){
let res;
res = await add(n); //返回4
res = await add(res); //返回8
res = await add(res); //返回16
console.log(res);
}
grow(2);//执行函数
以上代码输出结果:16
结果发现,给函数增加了 async、await 关键字,就可以直接获取promise对象返回的结果了,而不用使用then来接收结果。
上面函数的执行,看起来就好像是同步函数执行一样,这就是异步函数同步化处理。
注意:
1.必须增加 async、await 关键字;
2.异步函数同步化,其实代码逻辑只是看起来像普通的顺序执行逻辑,实际上每一步仍然需要等待成功返回,本质上仍然是异步的。
3.以上代码为了简化,缺少了reject失败的部分,添加reject部分后,只需要把执行任务的部分包在try...catch...里就可以了。
下面是调整后的代码:
//累加任务
function add(n) {
return new Promise(function (resolve, reject) {
if(true)
resolve(n + n);
else
reject('error infomation.');
});
}
//定义异步函数
async function grow(n, m){
let res = n;
try{
for (let i=0; i<m; i++) {
res = await add(res);
}
console.log(res);
}
catch(err){
console.log(err);
}
}
grow(2, 3);//执行函数,输出结果:16
Promise总结
1.使用promise对象的基本方式是:
new Promise(function).then(res=>{ ... }).catch(err=>{ ... });
任务function执行成功后的返回值res,执行失败后的返回值err;
任务成功后,就执行then,对res进行处理;
任务失败后,就执行catch,对err进行处理;
2.Promise对象可以链式调用:
job1.then(job2).then(job3).then(res=>{ ... }).catch(err=>{ ... });
job1、job2、job3...必须都是promise对象。
3.Promise异步同步化,必须使用async、await关键字。
可以直接获取promise任务成功的返回值。