JavaScript学习笔记1:Promise、async和await,期约与异步函数

145 阅读4分钟

一、为什么使用Promise?

我们知道 js 执行的时候,一次只能执行一个任务,它会阻塞其他任务。由于这个缺陷导致 js 的所有网络操作,浏览器事件,都必须是异步执行。异步执行可以使用回调函数执行。

常见的异步模式有以下几种:

  • 定时器
  • 接口调用
  • 事件函数

image.png

image.png

上述定时器是在固定时间触发某个回调函数。

对于 ajax 网络请求就没有这么简单了,可能有多个网络请求是关联的,先执行某个请求返回结果后,第一个返回结果作为第二个请求的参数,调用第二个网络请求。如此,如果业务复杂,网络请求太多时,回调也很多,容易出现回调地狱。所以 Promise 出现了,专门解决异步回调地狱问题。

二、Promise基本使用

下列用到的所有定时器模拟我们的 ajax 请求。

Promise 实例化的时候,传入的参数是一个函数,函数中接收两个参数:resolve, reject

image.png

传入的 resolve 和 reject 本身都是函数。其作用分别为:

resolve - 把 Promise 的状态从进行中变为成功状态。

reject - 把 Promise 的状态从进行中变为拒绝状态。

Promise的三种状态:

pending :进行中,表示 Promise 还在执行阶段,没有执行完成。

fulfilled:成功状态,表示 Promise 成功执行完成。

rejected:拒绝状态,表示 Promise 执行被拒绝,也就是失败。

Promise 的状态,只可能是其中一种状态,从进行中变为成功或失败状态之后,状态就固定了,不会再发生改变。

Promise.then

执行 resolve 时,Promise 状态变为 fulfilled ,会执行 .then 方法。then 方法接收的参数也是一个函数,函数中携带一个参数,该参数是 resolve(res) 返回的数据。

Promise.catch

执行 reject 时,Promise 状态从 pending 变为 rejected,会执行 catch 方法,catch 方法接收的也是一个函数,函数中携带一个参数,该参数为 reject(err) 返回的数据。

三、用asyn/await来处理异步

async的用法,它作为一个关键字放到函数前面,只有一个作用, 它的调用会返回一个promise 对象。async 函数也是函数,所以它的调用和普通函数的调用没有什么区别,直接加括号调用就可以了。

<script>
    var p1 = new Promise((resolve, rej) => {
        // console.log('没有 resolve')
        //throw new Error('手动返回错误')
        rej('失败了')

    })

    p1.catch(
        res => {
            console.log('catch data1:', res)
        })

    async function p2(res, rej) {
        throw new Error('错了')
    }

    p2().catch(
        rej => {
            console.log('catch data2:', rej)
        })

    async function p3(res, rej) {
        return '成功了'
    }

    p3().then(
        res => {
            console.log(res)
        }
    )
</script>

控制台输出结果:

image.png

对比async 关键字创建的函数和普通的期约对象。

接下来关注await关键字。

如果promise对象通过then或catch方法又注册了回调函数,async函数执行完以后,注册的回调函数就会放到异步队列中,等待执行。如果只是async,  和promise 差不多,但有了await就不一样了, await 关键字只能放到async 函数里面,await是等待的意思,那么它等待什么呢,它后面跟着什么呢?其实它后面可以放任何表达式,不过我们更多的是放一个返回promise 对象的表达式,它等待的是promise 对象的执行完毕,并返回结果。

现在写一个函数,让它返回promise 对象,该函数的作用是2s 之后让数值乘以2

image.png

在没有使用await关键字等待时,控制台输出结果如下

image.png

在使用了await之后

image.png

控制台1.5秒后输出220

image.png

这里强调一下等待,当js引擎在等待promise resolve 的时候,它并没有真正的暂停工作,它可以处理其它的一些事情,如果我们在testResult函数的调用后面,console.log 一下,你发现后面console.log的代码先执行。

image.png

控制台输出:

image.png

四、宏任务与微任务

微任务: Promise, async/await 宏任务: setTimeOut, setTimeInterval, DOM事件, Ajax 在执行栈执行完毕之后 EventLoop事件循环之前,这二者之间有一个浏览器尝试渲染DOM的过程
微任务的执行是在渲染DOM之前,宏任务的执行是在渲染DOM之后。
注意DOM渲染和DOM结构改变是不一样的,异步的是DOM的渲染,DOM结构的改变是同步的???

从EventLoop解释 为什么微任务执行更早?

image.png

image.png

image.png 微任务是ES语法规定的,宏任务是浏览器规定的。

场景题 async/await 语法

image.png

**第一题 ** a等于一个Promise
b等于100

第二题

打印start

  • await后面跟一个数字相当于直接返回这个数字,因此a是100
  • await后面跟一个promise.resolve() await相当于.then 因此b是200
  • await之后跟一个Promise.reject() 会报错 代码中断在这里。

then和catch的返回

如果then和catch代码中没有抛出错误也没有返回值 那么返回的就是一个resolve的Promise
如果抛出错误 那么返回的就是一个reject的Promise