function * greneratorDome(){yield "Hello";yield "World";return "Ending";}let grenDome = greneratorDome();console.log(grenDome.next());// {value: "Hello", done: false}console.log(grenDome.next());// {value: "World", done: false}console.log(grenDome.next());// {value: "Ending", done: true}console.log(grenDome.next());// {value: undefined, done: true}粗略实现 Generator
function makeIterator(array) {var nextIndex = 0;return {next: function() {return nextIndex < array.length ?{value: array[nextIndex++], done: false} :{value: undefined, done: true};}};}var it = makeIterator(['a', 'b']);it.next() // { value: "a", done: false }it.next() // { value: "b", done: false }it.next() // { value: undefined, done: true }Async/与 Generator类似,Async/await是 Javascript编写异步程序的新方法 。以往的异步方法无外乎回调函数和 Promise 。但是 Async/await建立于Promise之上,个人理解是使用了 Generator函数做了语法糖 。async函数就是隧道尽头的亮光,很多人认为它是异步操作的终极解决方案 。
function a(){return new Promise((resolve,reject) => {console.log("a函数")resolve("a函数")})}function b (){return new Promise((resolve,reject) => {console.log("b函数")resolve("b函数")})}async function dome (){let A = await a();let B = await b();return Promise.resolve([A,B]);}dome().then((res) => { console.log(res);});
Node.js异步I/O
当我们发起 IO请求时,调用的是各个不同平台的操作系统内部实现的线程池内的线程 。这里的 IO请求可不仅仅是读写磁盘文件,在 *nix中,将计算机抽象了一层,磁盘文件、硬件、套接字等几乎所有计算机资源都被抽象为文件,常说的 IO请求就是抽象后的文件 。完成 Node整个异步 IO环节的有事件循环、观察者、请求对象 。
事件循环机制
单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务 。如果前一个任务耗时很长,后一个任务就不得不一直等着 。于是就有一个概念,任务队列 。如果排队是因为计算量大,CPU忙不过来,倒也算了,但是很多时候 CPU是闲着的,因为 IO设备(输入输出设备)很慢(比如 Ajax操作从网络读取数据),不得不等着结果出来,再往下执行 。
事件循环是 Node的自身执行模型,正是事件循环使得回调函数得以在 Node中大量的使用 。在进程启动时 Node会创建一个 while(true)死循环,这个和 Netty也是一样的,每次执行循环体,都会完成一次 Tick 。每个 Tick的过程就是查看是否有事件等待被处理 。如果有,就取出事件及相关的回调函数,并执行关联的回调函数 。如果不再有事件处理就退出进程 。

文章插图
线程只会做一件事情,就是从事件队列里面取事件、执行事件,再取事件、再事件 。当消息队列为空时,就会等待直到消息队列变成非空 。而且主线程只有在将当前的消息执行完成后,才会去取下一个消息 。这种机制就叫做事件循环机制,取一个消息并执行的过程叫做一次循环 。
while(true) {var message = queue.get();execute(message);}我们可以把整个事件循环想象成一个事件队列,在进入事件队列时开始对事件进行弹出操作,直至事件为 0为止 。process.nextTick
process.nextTick()方法可以在当前"执行栈"的尾部-->下一次 EventLoop(主线程读取"任务队列")之前-->触发 process指定的回调函数 。也就是说,它指定的任务总是发生在所有异步任务之前,当前主线程的末尾 。( nextTick虽然也会异步执行,但是不会给其他 io事件执行的任何机会);
process.nextTick(function A() {console.log(1);process.nextTick(function B(){console.log(2);} );});setTimeout(function C() {console.log(3);}, 0);// 1// 2// 3 异步过程的构成要素异步函数实际上很快就调用完成了,但是后面还有工作线程执行异步任务,通知主线程,主线程调用回调函数等很多步骤 。我们把整个过程叫做异步过程,异步函数的调用在整个异步过程中只是一小部分 。
一个异步过程的整个过程:主线程发一起一个异步请求,相应的工作线程接收请求并告知主线程已收到通知(异步函数返回);主线程可以继续执行后面的代码,同时工作线程执行异步任务;工作线程完成工作后,通知主线程;主线程收到通知后,执行一定的动作(调用回调函数) 。
它可以叫做异步过程的发起函数,或者叫做异步任务注册函数 。args是这个函数需要的参数,callbackFn(回调函数)也是这个函数的参数,但是它比较特殊所以单独列出来 。所以,从主线程的角度看,一个异步过程包括下面两个要素:
推荐阅读
- JavaScript之dayjs用法,替代moment.js
- 10个很棒的JavaScript库,提升Web开发效率
- 同步与异步Python有何不同?
- python 中日志异步发送到远程服务器
- JavaScript 里的奇葩知识
- linux异步IO编程实例分析
- 浅析Kubernetes网络模型
- 对于 JavaScript 中循环之间的技术差异概述
- Java8——异步编程
- 开源JavaScript实用日期处理库——date-fns
