说说异步

异步出现的前提

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

异步如何实现

因为是单线程语言,因此JavaScript实现异步的方法是经过**事件循环(event loop)**来实现异步.html

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

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

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

这段代码的运行结果为java

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

因此,代码并无是自上而下执行,setTimeout函数是延迟了一段时间,等其余语句执行完了采起执行,这种状况就为异步。es6

eventloop的机制

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

因此JavaScript的执行机制实际上是:promise

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

这个循环即为 event loop浏览器

异步也有小区别

咱们先看一段代码bash

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,1async

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

因此任务应该分为这两类:

  • 宏任务(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

欢迎学习交流

相关文章
相关标签/搜索