JavaScript异步编程
JavaScript异步编程的进化历程:callback -> Promise -> Generator -> async/await
异步的意思是该代码与其他代码的关系是异步执行的,而在该代码自己内部是同步执行的。
js的异步机制:
所有任务可以分成两种,一种是同步任务(synchronous),另一种是异步任务(asynchronous)。同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;异步任务指的是,不进入主线程、而进入”任务队列”(task queue)的任务,只有”任务队列”通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。
- 所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。
- 主线程之外,还存在一个”任务队列”(task queue)。只要异步任务有了运行结果,就在”任务队列”之中放置一个事件。
- 一旦”执行栈”中的所有同步任务执行完毕,系统就会读取”任务队列”,看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。
- 主线程不断重复上面的第三步。
回调函数(callback)
回调函数是js中常见的异步编程方式,比如定时器:
1 | // 会先执行f1的代码,再将传入的f2作为回调函数执行。 |
除了 setTimeout
之外,还有 XMLHttpRequest
也使用了回调函数编写异步任务。
事件监听(EventListener)
比如由f1运行一段任务,任务完成后触发一个事件。
f2监听这个事件,监听到了就运行任务。
1 | // 给f1绑定事件和触发函数f2 |
发布/订阅(publish/subscribe)
有一个信号中心,生产者发布信号(publish)到信号中心,消费者订阅信号中心的信号(subscribe)。
消费者还可以取消订阅信号(unsubscribe)。
Promise
Promise
可以将回调套娃的代码变为扁平的顺序结构。
Promise对象的状态只能从pending到resolve或者从pending到reject。
注意: then()
接受两个参数,第一个参数是resolve时调用的函数,第二个参数是reject时调用的函数。
比如:
1 | // Promise的参数是一个函数,这个函数会被异步执行 |
jQuery异步编程API:
基于Promise的when, then, done, fail
参考我之前写的:jQuery的when, then, done, fail
Generator
Generator
用于创建迭代器(iterator)。
关于iterator:
- iterator为不同的可迭代的对象提供统一的访问接口。
- 可以按照次序迭代可迭代对象。
- 可以用for…of语句迭代。
- iterator内有next方法,next内有value(当前迭代的值)和done(是否迭代完成)属性。
创建迭代器:
1 | let arr = ['a', 'b', 'c']; |
创建Generator并使用:
1 | // 创建Generator,执行后返回一个iterator对象 |
Generator可以在运行过程中使用 yield
多次return,故而可以异步编程。
注意:Generator需要配合 yield
和 *
使用。
异步函数(async/await)
async/await是JavaScript异步编程的终极解决方案。
异步函数的返回值是一个Promise对象。
在异步函数内部,可以使用 await
命令等待一个Promise对象处理的结果(await也可以等待其他数据类型的字面量),再顺序往下执行。
1 | function asyncFunction() { |
e.g.
1 | async function a() { |
参考文档
- https://www.runoob.com/w3cnote/es6-promise.html
- https://www.runoob.com/js/js-promise.html
- https://www.runoob.com/js/js-async.html
- https://www.runoob.com/w3cnote/es6-async.html
- https://www.ruanyifeng.com/blog/2012/12/asynchronous%EF%BC%BFjavascript.html
- https://mp.weixin.qq.com/s/tGfC5XVuWXuSbG7wFLuaag