【JS】回调函数、回调地狱、Promise基本概念、async/await关键字
回调函数
回调函数就是一个被作为参数传递的函数。
说的通俗点就是,当一个函数A作为参数,被传入另一个函数B中,并且它不会立刻执行,而是只有当满足一定条件后函数A才可以执行,那么像A这种函数就称为回调函数。
像定时器和ajax中就存在有回调函数。
//定义一个函数
var A = function (){
console.log('我是回调函数A');
}
//把函数当作参数传递进另一个函数
setTimeout(A,3000);
同步任务和异步任务
同步任务
如下面的代码:
console.log('最先执行我');
console.log('然后执行我');
console.log('最后执行我');
上面代码就是一个同步任务的示例,每个console语句就是一个“任务”,同步任务在主线程上排队执行,只有当前一个任务执行完毕,下一个任务才能执行(也可以理解为就是按代码的先后顺序执行)。
异步任务
异步任务与同步任务相对应,异步任务不进入主线程,而是进入异步队列中,前一个任务是否执行完毕并不影响下一个任务的执行。
setTimeout(function(){
console.log('我虽然在代码的位置靠前,但是我后执行');
},3000);
console.log('我虽然在代码最后后面,但是我比上面那个console先执行');
像这种不会阻塞后面任务执行的任务,就叫做异步任务。
上面的两个示例,我们可以推论出一个结果:如果代码中存在异步任务,那我们就不能保证代码会按照顺序执行。
那么如果我们杠一下,非要让存在异步任务的代码按顺序执行呢?这就是下面讲的回调地狱了。
回调地狱
既然是涉及到顺序,那么用我们汉语的语序做例子最容易理解了。比如说有一句话,“大家好,我是,蜻蜓队长”,要用异步的方式正确的输出这句话,就需要像下面这样写:
setTimeout(function(){
console.log('大家好');
setTimeout(function(){
console.log('我是');
setTimeout(function(){
console.log('蜻蜓队长');
},1000);
},2000);
},3000);
像上面这样的代码,在回调函数中嵌套回调函数的情况就叫做回调地狱。
回调地狱是为了实现代码顺序执行而出现的一种操作,虽然这样确实实现了我们的需求,按顺序输出了我们要说的话,但是这样会造成我们的代码可读性非常差,维护起来难死了。
那么有啥办法解决回调地狱呢?就是我们下面的Promise了。
Promise
Promise是js中的一个原生对象(其实是一个构造函数),是一种异步编程的解决方案,可以替换掉传统的回调函数解决方案,其自身有all、reject、resolve这些方法,原型上有then、catch等方法。
Promise构造函数
Promise构造函数接收一个函数A作为参数。 函数A的两个参数是 resolve、reject
,函数A的函数体就是我们要处理的异步任务,异步任务执行成功时调用resolve函数返回结果,否则的话就调用reject函数返回结果。
Promise对象
Promise对象的then方法用来接收处理成功时响应的数据,catch方法则用来接收处理失败时响应的数据。
通过在每一次then做完处理后,return一个Promise对象,可以实现Promise的链式编程,进而保证代码的执行顺序。
Promise对象的两个特点
Promise对象的状态不受外界影响。 Promise对象代表一个异步操作,有三种状态:
- pending(进行中)
- fulfilled(已成功)
- rejected(已失败)
只有异步操作的结果可以决定当前的状态是哪一种,其他任何操作都无法改变这个状态。
Promise对象的状态一旦改变就不会再变,任何时候都可以得到这个结果。 Promise对象状态的改变只会有两种可能:
- pending -> fulfilled
- pending -> rejected
只要这两种状态发生,对象的状态就凝固了,不会再改变了(一直保持这个状态),这时称为 resolved(已定型),如果改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果。
上面这个特性也是Promise对象与事件Event不同的地方,事件的特点是,如果你错过了它,再去监听,是得不到结果的。
通俗点讲,一个人本来是单身状态,然后状态改变了,变成了已婚状态(这时改变以及发生了),此时你再去查看这个人的状态(添加回调函数),会得到他已婚的结果,而事件则啥也查看不到。
使用Promise使得我们的代码看起来整洁了那么一点点,但是也仅仅是一点点,当代码量多起来的时候,比如我要蜻蜓队长唱一首歌,那岂不是满屏的then...then...。
因此,在ES7中,可以使用 async/await
关键字使得我们的代码看起来更像同步代码。
async/await关键字
async关键字
放在声明函数的前面,表示该函数作为一个异步任务,不会阻塞后面函数的执行。
const isSpeak = true;//给不给蜻蜓队长说话
async function Speak(Str){
if(isSpeak){
return Str;
}else{
throw '不给蜻蜓队长说话';
}
}
console.log(Speak());
从上面的结果可以看出,async关键字声明的函数返回数据时,自动封装为一个Promise对象,所以处理异步任务时和上上面的方式一样,使用then和catch。
await关键字
wait => 等待
,当代码执行到 async 函数中的await时,代码就会在此处等待,直到await拿到了promise对象中的resolve的数据,才会继续往下执行,这样保证了代码的执行顺序,并且使得异步代码看起来更像同步代码。
使用方法
await
关键字只能用在async
声明的函数里面;await
关键字后面可以直接跟一个Promise实例对象,也可以跟一个表达式(返回一个Promise对象的表达式);await
关键字不能单独使用;await
关键字可以直接拿到Promise中resolve
的数据;
await
关键字是不会处理 Promise对象的 reject(err)
的,比如下面不让蜻蜓队长说话。
这个时候就需要用到
try...catch...
来处理了。
完结
转载自:https://juejin.cn/post/7107251790802305054