异步操作

同步任务是那些没有被引擎挂起、在主线程上排队执行的任务。只有前一个任务执行完毕,才能执行后一个任务。异步任务是那些被引擎放在一边,不进入主线程、而进入任务队列的任务。只有引擎认为某个异步任务可以执行了(比如 Ajax 操作从服务器得到了结果),该任务(采用回调函数的形式)才会进入主线程执行。异步操作执行顺序可详见进阶知识-事件循环机制

1. 回调函数 Callback

function f1(callback){
  // ...
  callback();
}

function f2(){
  // ...
}

f1(f2); // f1若为异步操作,则f2会等待f1执行完毕才运行,正常js引擎遇到异步操作,会挂起,并运行下一个任务

2. 事件监听

异步任务的执行不取决于代码的顺序,而取决于某个事件是否发生。

具体详见《异步操作-事件监听》

3. 发布/订阅

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

具体详见《异步操作-发布订阅》

4. 定时器

2.1 setTimeout()

第一个参数 func|code 是将要推迟执行的函数名或者一段代码,第二个参数 delay 是推迟执行的毫秒数。返回一个整数,表示定时器的编号,以后可以用来取消这个定时器。

2.2 setInterval()

与setTimeout()用法一致,setInterval指定某个任务每隔一段时间就执行一次,也就是无限次的定时执行。setTimeout函数用来指定某个函数或某段代码,在多少毫秒之后执行。

setTimeoutsetInterval函数都返回一个整数值,表示计数器编号,将该整数传入clearTimeoutclearInterval函数,就可以取消对应的定时器。

5. ES6 - Promise*

参考链接:ES6-promise

Promise 对象一旦新建它就会立即执行,无法中途取消。

then 方法通过链式方法可读性更强,then 方法可以接受两个回调函数,第一个是异步操作成功时(变为fulfilled状态)的回调函数,第二个是异步操作失败(变为rejected)时的回调函数(一般该参数可以省略)

Promise.prototype.catch 方法是 .then(null, rejection).then(undefined, rejection) 的别名,用于指定发生错误时的回调函数。因此在使用上建议 then 就使用单个参数表示成功回调函数,错误回调函数一律通过 catch 调用。

finally方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。

  • Promise.all()并发处理多个异步任务,所有任务都执行成功,才能得到结果。

  • Promise.race()并发处理多个异步任务,只要有一个任务执行成功,就能得到结果。

链式调用,基于 Promise 处理多层嵌套调用:

return 后面的返回值,有两种情况:

  • 情况 1:返回 Promise 实例对象,则该实例对象会调用下一个 then

  • 情况 2:返回普通值,则直接传递给下一个 then,通过 then 参数中函数的参数接收该值

6. ES6- Generator

参考资料:ES6-Generator

Generator 函数是一个状态机,封装了多个内部状态;Generator 函数也是一个遍历器对象生成函数,返回遍历器对象,可以依次遍历 Generator 函数内部的每一个状态。

next 方法可以带参数,该参数就会被当作上一个 yield 表达式的返回值(第一次使用 next 方法时无上一个yeild,则传递参数是无效的),若不带参数,yield表达式对应值为undefined。

内部调用的是遍历器接口的都可以将 Generator 函数返回的 Iterator 对象作为参数,但是注意返回对象的done属性为true,遍历循环就会中止,且不包含该返回对象,也就是说return后面所带的不包含在内。

Generator 函数返回的遍历器对象的方法:

  • next() - 使得指针移向下一个状态,见上面的一些应用

  • throw() - 在函数体外抛出错误,然后在 Generator 函数体内捕获。

  • return() - 可以返回给定的值,并且终结遍历 Generator 函数。

这三个方法本质上是同一件事,它们的作用都是让 Generator 函数恢复执行,并且使用不同的语句替换 yield 表达式。

yield* 表达式用来在一个 Generator 函数里面执行另一个 Generator 函数。

yield* 后面的 Generator函数,没有 return 语句时,相当于 for...of 的一种简写形式;在有 return 语句时,则需要用 var value = yield* iterator 的形式获取 return 语句的值。

实际上,任何数据结构只要有 Iterator 接口,就可以被 yield* 遍历。

7. ES6 - async/await*

async 返回的是一个 promise 对象,如果 return 一个直接量,也会通过 Promise.resolve() 封装成 Promise 对象。await 后面接 Promise 对象,它会阻塞后面的代码,等着 Promise 对象 resolve,然后得到 resolve 的值,作为 await 表达式的运算结果;如果后面是不是一个 Promise 对象,则表达式的运算结果就是它等到的东西。

async 函数返回一个 Promise 对象,可以使用 then 方法添加回调函数。当函数执行的时候,一旦遇到 await 就会先返回,等到异步操作完成,再接着执行函数体内后面的语句。

async 函数是 Generator 函数的语法糖,其中几点改进:

  • Generator 函数的执行必须必须靠执行器 next(),而 async 函数的执行与普通函数一样,只要一行。

  • async 表示函数里有异步操作,await 表示紧跟在后面的表达式需要等待结果。

  • Generator 函数中 yield 命令后面只能是 Thunk 函数或 Promise 对象,async 函数的 await 命令后面,可以是 Promise 对象和原始类型的值(实际会转成立即 resolved 的 Promise 对象)。

语法的注意点:

  • async 函数内部 return 语句返回的值,会成为 then 方法回调函数的参数。

  • 正常情况下,await 命令后面是一个 Promise 对象,返回该对象的结果。如果不是 Promise 对象,就直接返回对应的值。

  • await 命令后面的 Promise 对象,运行结果可能是 rejected,所以最好把 await 命令放在 try...catch 代码块中。

链式调用,基于 async/await 处理多层嵌套调用:

如果你对内容有任何疑问,欢迎提交 ❕issues✉️ email

最后更新于

这有帮助吗?