Promise从入门到拿Offer之async 与 await、宏队列与微队列

1、async函数

函数的返回值为promise对象,promise对象的结果由async函数执行的返回值决定。如果任何一个await语句后面的 Promise 对象变为reject状态或遇到return,那么整个async函数都会中断执行。

await 在等待 Promise 对象时会导致 async function 暂停执行, 一直到 Promise 对象决议之后才会 async function 继续执行。

2、await表达式

await右侧的表达式一般为promise对象,但也可以是其他的值;

如果表达式是promise对象,await返回的是promise成功的值;如果表达式是其他值,直接就将此值作为await的返回值。

注意:await必须写在async函数中,但async函数中可以没有await;如果await的promise失败了,就会抛出异常,需要通过try...catch捕获处理。

常见的场景:

场景1. 一个请求接着一个请求;

场景2.并发请求;

场景3.错误处理;

场景4. 超时处理;

场景5. 并发限制。

3、宏队列与微队列

JS引擎执行图

JS中包含两个不同的队列用来存储待执行的回调函数,即宏队列和微队列。

宏队列用来保存待执行的宏任务。如定时器回调、DOM事件回调、Ajax回调。

微队列用来保存待执行的微任务,比如promise回调、mutationObserver()的回调。

JS引擎执行顺序:

1)首先执行初始化的同步代码块,遇到回调函数,需保存到对应的队列中。

2)执行微队列中的微任务。在此也可能继续存在不同的回调函数,需继续存入相应的队列。

3)每次准备执行取出第一个宏任务执行前,检查微队列是否存在微任务待执行,如果存在,则先执行微任务。

示例分析:
```

1 console.log("AAAA");

2 setTimeout(() => console.log("BBBB"), 1000);

3 const start = new Date();

4 while (new Date() - start < 3000) {}

5 console.log("CCCC");

6 setTimeout(() => console.log("DDDD"), 0);

7 new Promise((resolve, reject) => {

8  console.log("EEEE");

9  foo.bar(100);

10 })

11 .then(() => console.log("FFFF"))

12 .then(() => console.log("GGGG"))

13 .catch(() => console.log("HHHH"));

14 console.log("IIII");

```

一开始代码执行,输出`AAAA`. 1

第二行代码开启一个计时器t1(一个称呼),这是一个异步任务且是宏任务,需要等到1秒后提交。

第四行是个while语句,需要等待3秒后才能执行下面的代码,这里有个问题,就是3秒后上一个计时器t1的提交时间已经过了,但是线程上的任务还没有执行结束,所以暂时不能打印结果,所以它排在宏任务的最前面了。

第五行又输出`CCCC`

第六行又开启一个计时器t2(称呼),它提交的时间是0秒(其实每个浏览器器有默认最小时间的,暂时忽略),但是之前的t1任务还没有执行,还在等待,所以t2就排在t1的后面。(t2排在t1后面的原因是while造成的)都还需要等待,因为线程上的任务还没执行完毕。

第七行`new Promise`将执行promise函数,它参数是一个回调函数,这个回调函数内的代码是同步的,它的异步核心在于resolve和reject,同时这个异步任务在任务队列中属于微任务,是优先于宏任务执行的,(不管宏任务有多急,反正我是VIP)。所以先直接打印输出同步代码`EEEE`。第九行中的代码是个不存在的对象,这个错误要抛给reject这个状态,也就是catch去处理,但是它是异步的且是微任务,只有等到线程上的任务执行完毕,立马执行它,不管宏任务(计时器,ajax等)等待多久了。

第十四行,这是线程上的最后一个任务,打印输出 `IIII`

我们先找出线程上的同步代码,将结果依次排列出来:AAAA  CCCC  EEEE IIII

然后我们再找出所有异步任务中的微任务 把结果打印出来  HHHH

最后我们再找出异步中的所有宏任务,这里t1排在前面t2排在后面(这个原因是while造成的),输出结果顺序是 BBBB DDDD

所以综上 结果是  AAAA  CCCC  EEEE  IIII  HHHH BBBB DDDD

4、JS的异步处理函数有哪些

1)Promise 对象

Promise是异步编程的一种解决方案,

优点: 相比传统回调函数和事件更加合理和优雅,Promise是链式编程(后面会详细讲述),有效的解决了令人头痛的回调地狱问题,Promise的结果有成功和失败两种状态,只有异步操作的结果,可以决定当前是哪一种状态,外界的任何操作都无法改变这个状态。

2)Generator 函数

 Generator 函数是 ES6 提供的一种异步编程解决方案,语法行为与传统函数完全不同。

特征:一是,function关键字与函数名之间有一个星号;

二是,函数体内部使用yield表达式,定义不同的内部状态;

三是,通过next方法获取每段状态的返回结果。

上面分成4次执行了Gennrator函数,第一次,获取第一个yield函数的返回结果并暂停,done:false,代表函数内的状态还没有执行结束;第二次,同理;第三次,获取return的返回结果,done:true表示函数内的状态已经执行完毕;第四次,函数内已经没有可以执行的状态,所有返回undfined,同时告诉函数内的状态已经执行完毕;如果函数内没有return,在第三次时返回undfined,done:true表示函数内的状态已经执行完毕。

3)async 函数

async 函数是在ES2017 标准中引入的,async使得异步操作变得更加方便,其实他就是Generator 函数的语法糖。

 相比Generator函数,async函数有如下四点改进:

a、内置执行器: Generator 函数的执行必须靠next()进行每一次的模块执行,async自带执行器,只需要和普通函数一样调用即可执行

b、更好的语义:async表示函数里有异步操作,await表示紧跟在后面的表达式需要等待结果。

c、返回值是Promise: async函数的返回值是 Promise 对象,可以用then方法指定下一步的操作;而且async函数完全可以看做多个异步函数的操作,包装成的一个 Promise对象,而await命令就是内部then命令的语法糖,即Promise.all()的用法

4)回调函数   

5)事件监听

采用事件驱动模式。任务的执行不取决于代码的顺序,而取决于某个事件是否发生。

6)发布订阅模式

存在一个"信号中心",某个任务执行完成,就向信号中心"发布"(publish)一个信号,其他任务可以向信号中心"订阅"(subscribe)这个信号,从而知道什么时候自己可以开始执行。这就叫做"发布/订阅模式"(publish-subscribe pattern),又称"观察者模式"(observer pattern)

你可能感兴趣的:(Promise从入门到拿Offer之async 与 await、宏队列与微队列)