一、回调函数
回调函数是一种特殊的函数,它作为参数传递给另一个函数,并在被调用函数执行完毕后被调用。回调函数通常用于事件处理、异步编程和处理各种操作系统和框架的API。
简单来说:回调函数是一个作为参数传递给其他函数的函数,它能够被异步调用以处理某些事件或完成某些任务。
setTimeout(function() {
console.log(1);
setTimeout(function() {
console.log(2);
setTimeout(function() {
console.log(3);
setTimeout(function() {
console.log(4);
setTimeout(function() {
console.log(5);
}, 1000);
}, 1000);
}, 1000);
}, 1000);
}, 1000);
过多的嵌套回调函数会带来回调地狱
二、Promise
- promise是为了解决异步编程的回调地狱问题,promise是一个对象,它可以获取异步操作的消息。
- promise对象可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。此外,promise对象提供统一的接口,使得控制异步操作更加容易。
- promise是解决异步的方法,本质上是一个构造函数,可以用它实例化一个对象。
- 对象身上有resolve、reject、all,原型上有then、catch方法。
- promise对象有三种状态:pending(初识状态/进行中)、resolved或fulfilled(成功)、rejected(失败)
- 特点
- 对象状态不受外界的影响,只有异步操作的结果可以决定当前是哪一种状态
- 一旦状态改变,就不会再变,任何时候都可以得到这个结果
1. 创建promise 实例
const p=new Promise(function(resolve,reject){
setTimeout(function(){
console.log('success')
resolve('end')
},2000)
})
Promise接收一个函数,并传入resolve、reject两个参数,表示异步操作执行成功、失败后的回调函数。
上述代码没有调用就执行了,因此promise通常是包在函数中使用
2. 函数内的promise
function runAsync(){
const p=new Promise(function(resolve,reject){
setTimeout(function(){
console.log('success')
resolve('end')
},2000)
})
return p
}
runAsync()
运行结果:
包装后的函数会return出promise对象,即可以使用promise上的方法
以Promise.then() 方法为例
function runAsync(){
const p=new Promise(function(resolve,reject){
setTimeout(function(){
console.log('success')
resolve('end')
},2000)
})
return p
}
runAsync().then(function(data){
console.log(data)
})
运行结果:
then能够在runAsync这个异步任务完成之后被执行,其中接收一个函数,可以拿到runAsync在调用resolve时传的参数。即可以把原来的回调函数分离出来,在异步操作执行完后,用链式调用的方式执行回调函数。
使用场景:稍微复杂一些的链式调用
runAsync1()
.then(function(data){
console.log(data);
return runAsync2();
})
.then(function(data){
console.log(data);
return runAsync3();
})
.then(function(data){
console.log(data);
});
每一个.then接收到上一个异步任务完成后的resolve中传递过来的值
参考文章:https://www.cnblogs.com/lvdabao/p/es6-promise-1.html
三、Promise的方法
1、Promise.prototype.then()
- 作用:为promise实例添加状态改变时的回调函数;
- then方法一般接收两个参数,一个成功的回调函数,一个失败的回调函数
runAsync().then(
function(data){
console.log('resolved:',data)
},
function(data){
console.log('rejected:',data)
})
第一个
then
方法指定的回调函数,返回的是另一个Promise
对象。这时,第二个then
方法指定的回调函数,就会等待这个新的Promise
对象状态发生变化。如果变为resolved
,就调用第一个回调函数,如果状态变为rejected
,就调用第二个回调函数。
//改写为箭头函数
runAsync().then
(
data=> console.log('resolved:',data),
err=>console.log('rejected:',err)
)
- then方法定义在原型对象上;
2、Promise.prototype.catch()
- 作用:与.then中第二个参数相同,用于指定发生错误时的回调函数
- 捕捉promise错误函数,同时catch还会捕捉then中第一个参数(函数)抛出的异常
new Promise((resolve, reject) => {
reject();
}).catch((err)=> {
console.log(err)
});
3、Promise.prototype.finally()
- 作用:不管promise对象最后的状态如何,都会执行的操作
- finally方法的回调函数不接受任何参数,即不知道前面promise的状态,因此该方法时与状态无关的
promise
.then(result => {···})
.catch(error => {···})
.finally(() => {···});
promise
.finally(() => {
// 语句
});
// 等同于
promise
.then(
result => {
// 语句
return result;
},
error => {
// 语句
throw error;
}
);
4、Promise.all()
用于将多个Promise实例,包装成一个新的Promise实例
const p = Promise.all([p1, p2, p3]);
上面代码中,
Promise.all()
方法接受一个数组作为参数,p1
、p2
、p3
都是 Promise 实例,如果不是,就会先调用下面讲到的Promise.resolve
方法,将参数转为 Promise 实例,再进一步处理。另外,Promise.all()
方法的参数可以不是数组,但必须具有 Iterator 接口,且返回的每个成员都是 Promise 实例。
p
的状态由p1
、p2
、p3
决定,分成两种情况。
(1)只有p1
、p2
、p3
的状态都变成fulfilled
,p
的状态才会变成fulfilled
,此时p1
、p2
、p3
的返回值组成一个数组,传递给p
的回调函数。
(2)只要p1
、p2
、p3
之中有一个被rejected
,p
的状态就变成rejected
,此时第一个被reject
的实例的返回值,会传递给p
的回调函数。
5、Promise.race()
用于将多个Promise实例,包装成一个新的Promise实例
const p = Promise.race([p1, p2, p3]);
上面代码中,只要
p1
、p2
、p3
之中有一个实例率先改变状态,p
的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p
的回调函数。
Promise.race()
方法的参数与Promise.all()
方法一样,如果不是 Promise 实例,就会先调用下面讲到的Promise.resolve()
方法,将参数转为 Promise 实例,再进一步处理。
6、Promise.allSettled()
Promise.allSettled()
方法接受一个数组作为参数,数组的每个成员都是一个 Promise 对象,并返回一个新的 Promise 对象。只有等到参数数组的所有 Promise 对象都发生状态变更(不管是fulfilled
还是rejected
),返回的 Promise 对象才会发生状态变更。
7、Promise.any()
- 只要参数实例有一个变成
fulfilled
状态,包装实例就会变成fulfilled
状态;如果所有参数实例都变成rejected
状态,包装实例就会变成rejected
状态。 Promise.any()
跟Promise.race()
方法很像,只有一点不同,就是Promise.any()
不会因为某个 Promise 变成rejected
状态而结束,必须等到所有参数 Promise 变成rejected
状态才会结束。
参考文章:ES6 入门教程