说说异步

异步出现的前提

首先,因为JavaScript语言是单线程的*(目标为浏览器端,出生即为单线程)*,所以就需要异步,否则JavaScript脚本智能自上而下执行,如果在上部存在一些极其复杂的代码需要解析很长的时间的话,下面的代码就会遭到阻塞,也就是用户感受到的卡死.

异步如何实现

由于是单线程语言,所以JavaScript实现异步的方法是通过**事件循环(event loop)**来实现异步.

console.log('I’m first one code!');

setTimeout(() => {
    console.log('I’m setTimeout function code!');
})

console.log('I’m last one code! ');
复制代码

这段代码的运行结果为

I’m first one code!
I’m last one code!
I’m setTimeout function code!
复制代码

所以,代码并没有是自上而下执行,setTimeout函数是延迟了一段时间,等其他语句执行完了采取执行,这种情况就为异步。

eventloop的机制

根据上一部分我们知道,JavaScript把事件分为两类:同步与异步

所以JavaScript的执行机制其实是:

  • 判断一个任务是同步任务还是异步任务,同步进入主线程,异步进入event table
  • 异步任务在event table中注册函数,满足该函数触发条件后,推入事件队列
  • 同步任务在主线程按顺序执行,当主线程空闲时,再去事件队列中查看是否有可执行的异步任务,如果有就进入主线程

这个循环即为 event loop

异步也有小区别

我们先看一段代码

setTimeout(() => {
    console.log('1');
});

new Promise((resolve) => {
    console.log('2');
}).then( () => {
    console.log('3');
});

console.log('4');
复制代码

如果按照我们刚才的理解,这段代码的结果应该为 2,4,1,3

But unfortunately!他的结果为 2,4,3,1

这也引入了另外两个概念:宏任务微任务

所以任务应该分为这两类:

  • 宏任务(macro-task): 包括整段script代码,setTimeout,setInterval
  • 微任务(micro-task): Promise,catch, finally,process.nextTick(Node端)

所以js的执行机制其实是

先执行一个宏任务,过程中如果遇到微任务先把他放到微任务的事件队列中,当宏任务执行完毕后,再去查看微任务的事件队列,将微任务一次执行完,执行完毕后再去进行下一个队列的宏任务,以此循环.

借用一张图演示

介绍主角Async

刚突然发现已经绕远了:car:,赶快漂移回原来的主题,async,一个在ES2017中提出的异步方案,有人说他是Generator函数的语法糖,只是把Generator函数的 * 替换为 async,把yield替换为await。我们先不讨论这句话说得对不对:speak_no_evil:,但它确实是基于了Generator的一种改进,它让异步变得更简单了。

他做了什么?

如果要简述的话,一旦你的函数前带上了async,你的函数返回值就必定是promise对象.(他就像真香定律一样是没有可以逃过的)就算你写的函数里返回的不是promise,他也会自动用Promise.resolve()包装起来让他成为一个promise对象。

所以,如果我们简单理解async关键字的话,他其实就是给函数加上一个标识,说明这个函数内部有异步操作。

What is await waiting for?

我们再次简单的介绍以下 await,await 其实等的是右侧表达式的结果.

如果右侧是一个函数,则是这个函数的返回值。如果是一个值则就为此值.

我们通过一个例子来"见识见识"它

async function fun1(){
    console.log('fun1 is started!');
    await fun2();
    console.log('fun1 ending!');
}

async function fun2(){
    console.log('fun2 is running!');
}
fun1();
console.log('script start');
复制代码

我们知道,await是通过执行到此时让出线程,通过阻塞后面的代码来执行,但我们执行上面的代码发现结果为

fun1 is started!
fun2 is running!
script start
fun1 endding!
复制代码

注意,这里fun2先于"script start" 执行,所以 await 的那个表达式的执行顺序其实是从右到左,即为执行了fun2后读到了await关键字,然后阻塞后面的代码,这点非常重要,因为之前因为"一旦遇到await就立马退出线程,阻塞后面的代码"的观点,认为 await也会阻塞他后面的那个表达式,但其实不然。

await 与 async 的关系就像鱼和水, await必须要有async才可以存在,而async却不一定需要有await。

await 的下一步操作

一般来说 await等到的右侧表达式结果有两种情况:

Promise or Not Promise。

  • 如果不是promise,await会阻塞之后的代码,就先去执行async外面的同步代码,同步代码执行完后再回到async内部,把这个不是promise的结果作为await的结果。
  • 如果是promise对象,await也会先阻塞async后面的代码,然后执行async外面的同步代码,等待这个promise对象到达fulfilled状态后,把 resolve 的参数作为 await的运算结果。其实就是执行了await Promise.resolve(),这里不做详细解释.

Async 与 Promise 和 Generator的一些比较

  • 首先Promise的提出是解决了之前令人头疼的回调地狱(callback hell)问题,但直观的看上去其实就像用了一个类库,通过Promise的api来完成了异步操作,操作本身的可调控性不是很高,但已经很实用了。
  • Generator函数的语义相较于Promise清晰了许多,但问题就是如果他要自动执行的话必须实用任务运行器来自动执行它。
  • Async相较于Generator函数实现起来相对简洁,更贴切语义。它在语言层面提供了Generator的自动执行器,代码量也大大减少。
参考链接

ECMAScript 6 标准入门

微任务、宏任务与Event-loop

javascript的宏任务与微任务

个人Github:Reaper622

欢迎学习交流

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值