Generator

熟悉ES6语法的同学们肯定对Generator(生成器)函数不陌生,这是一个化异步为同步的利器。
栗子:

function* abc() {let count = 0;while(true) {let msg = yield ++count;console.log(msg);}
}let iter = abc();
console.log(iter.next().value);
// 1
console.log(iter.next('abc').value);
// 'abc'
// 2

首先,我们先简单回顾一下JS的运行规则:

  1. JS是单线程的,只有一个主线程
  2. 函数内的代码从上到下顺序执行,遇到被调用的函数先进入被调用函数执行,待完成后继续执行
  3. 遇到异步事件,浏览器另开一个线程,主线程继续执行,待结果返回后,执行回调函数

那么,Generator函数是如何进行异步化为同步操作的呢?
实质上很简单,* 和 yield 是一个标识符,在浏览器进行软编译的时候,遇到这两个符号,自动进行了代码转换:

// 异步函数
function asy() {$.ajax({url: 'test.txt',dataType: 'text',success() {console.log("我是异步代码");}})
}function* gener() {let asy = yield asy();yield console.log("我是同步代码");
}
let it = gener().next();
it.then(function() {it.next();
})
// 我是异步代码
// 我是同步代码
// 浏览器编译之后
function gener() {// let asy = yield asy(); 替换为$.ajax({url: 'test.txt',dataType: 'text',success() {console.log("我是异步代码");// next 之后执行以下console.log("我是同步代码");}})// yield console.log("我是同步代码");
}

整个过程类似于,浏览器遇到标识符 * 之后,就明白这个函数是生成器函数,一旦遇到 yield 标识符,就会将以后的函数放入此异步函数之内,待异步返回结果后再进行执行。

更深一步,从内存上来讲:

普通函数在被调用时,JS 引擎会创建一个栈帧,在里面准备好局部变量、函数参数、临时值、代码执行的位置(也就是说这个函数的第一行对应到代码区里的第几行机器码),在当前栈帧里设置好返回位置,然后将新帧压入栈顶。待函数执行结束后,这个栈帧将被弹出栈然后销毁,返回值会被传给上一个栈帧。

当执行到 yield 语句时,Generator 的栈帧同样会被弹出栈外,但Generator在这里耍了个花招——它在堆里保存了栈帧的引用(或拷贝)!这样当 it.next 方法被调用时,JS引擎便不会重新创建一个栈帧,而是把堆里的栈帧直接入栈。因为栈帧里保存了函数执行所需的全部上下文以及当前执行的位置,所以当这一切都被恢复如初之时,就好像程序从原本暂停的地方继续向前执行了。

而因为每次 yield 和 it.next 都对应一次出栈和入栈,所以可以直接利用已有的栈机制,实现值的传出和传入。

至此,Generator 的魔力已经揭开。

Promise

Promise的用法大家应该都很熟悉:

let pr = new Promise(function(resolve, reject) {setTimeout(function() {resolve("成功执行啦");}, 2000)
})
pr.then(function(data) {console.log(data); // 成功执行啦
})

那么 Promise 是如何实现异步加载的呢?

Promise 并没有大家想的那么神秘,其本质就是一个状态机。

想要实现一个土生土长的 Promise 其实很简单,状态机,我们需要几个参数:

  • __success_res 用来存储成功时的参数
  • __error_res 用来存储失败时的参数
  • __status 用来存储状态
  • __watchList 用来存储执行队列

下面就手动实现一个 Promise

class Promise1 {constructor(fn) {// 执行队列this.__watchList = [];// 成功结果this.__success_res = null;// 失败结果this.__error_res = null;// 状态this.__status = "";fn((...args) => {// 保存成功数据this.__success_res = args;// 状态改为成功this.__status = "success";// 若为异步则回头执行then成功方法this.__watchList.forEach(element => {element.fn1(...args);});}, (...args) => {// 保存失败数据this.__error_res = args;// 状态改为失败this.__status = "error";// 若为异步则回头执行then失败方法this.__watchList.forEach(element => {element.fn2(...args);});});}// then 函数then(fn1, fn2) {if (this.__status === "success") {fn1(...this.__success_res);} else if (this.__status === "error") {fn2(...this.__error_res);} else {this.__watchList.push({fn1,fn2})}}
}

这样就简单实现了 Promise 的功能,在使用上和JS的 Promise 并无其他区别,若想实现 Promise.all 方法,则只需要进行小小的迭代:

Promise1.all = function(arr) {// 存放结果集let result = [];return Promise1(function(resolve, reject) {let i = 0;// 进行迭代执行function next() {arr[i].then(function(res) {// 存放每个方法的返回值result.push(res);i++;// 若全部执行完if (i === result.length) {// 执行then回调resolve(result);} else {// 继续迭代next();}}, reject)}})
}

至此,Generator 和 Promise 都已解析完成。

浅谈Generator和Promise原理及实现相关推荐

  1. 浅谈Rem 及其转换原理

    浅谈Rem 及其转换原理 今天有小伙伴问了我Rem的转换原理,那我就写篇博客记录一下吧! rem 是 CSS3 新增的相对长度单位,是指相对于元素 html 的 font-size 计算值 的大小. ...

  2. 浅谈人工智能的工作原理

    众所周知人工智能现在快速发展,并且为众人所熟知,不仅如此,人工智能也在各行各业中广泛使用.那么人工智能的工作原理是什么呢? 浅谈人工智能的工作原理 人类智能由三个部分构成(还有些其他生物学和科学现象也 ...

  3. 动态磅是怎么原理_浅谈动态地磅的原理及未来发展方向

    浅谈动态地磅的原理及未来发展方向: 文章介绍了动态地磅的结构和工作原理,针对动态地磅的分类做了全面的概述,分别对不同的动态地磅做了对比及详细的阐述,说明选择和使用动态地磅器的注意事项,凸显了轴组式动态 ...

  4. 浅谈 git 底层工作原理

    浅谈 git 底层工作原理 系统复习到这里也快差不多了,大概就剩下两三个 sections,这里学习一下 git 的 hashing 和对象. 当然,跳过问题也不大. config 文件 这里还是会用 ...

  5. 伺服驱动器生产文件_浅谈伺服驱动器的工作原理

    原标题:浅谈伺服驱动器的工作原理 目前,主流的伺服驱动器均采用数字信号处理器(DSP)作为控制核心,可以实现比较复杂的控制算法,实现数字化.网络化和智能化.功率器件普遍采用以智能功率模块(IPM)为核 ...

  6. 深入浅谈,CPU设计原理

    首先,声明这是一篇转载文,这篇文章是,从卡饭论坛 看到的一篇文章<深入浅谈,CPU设计原理>,是一篇连载,文章,卡饭论坛,是我高中的时候,经常去的论坛,里面有很多好的文章,推荐给大家.也许 ...

  7. 浅谈代理服务器工作的原理

    浅谈代理服务器工作的原理 (1) 代理服务原理 代理服务器有很多种,大体来说有http,ftp,socks代理三种,其中又分透明代理和不透明代理.其中透明代理一般是网关,是硬件.所以这里讨论不透明代理 ...

  8. 浅谈《微信抢红包原理》

    现在很多人手机可能都安装了抢红包软件,为了过年抢红包不错过,当然会下载来用用,其实,现在的抢红包软件,基本都是通过监听通知栏消息"[微信红包]"字样,作为识别是红包的依据的,可能大 ...

  9. 浅谈elasticsearch的分词原理

    这篇文章主要是来浅谈一下elasticsearch的分词原理,让各位同学对分词不再陌生~ 废话不多说,我们直接上干货 前言一 我们创建一个文档 PUT test/_doc/1 {"msg&q ...

最新文章

  1. php在dw中设置按钮圆角,Dreamweaver怎么用CSS制作圆角按钮?
  2. Pytorch之深入理解torch.nn.Parameter()
  3. CloudStack管理员文档 - 虚拟机
  4. Java Spring singleton bean的创建源代码
  5. 隐藏apache版本号的方法
  6. 虚拟化简化数据中心管理
  7. NOIP2002题目汇总
  8. 第五章---引入VIP后的数据库架构
  9. TokenInsight:反映区块链行业整体表现的TI指数较昨日同期下降3.29%
  10. JSON 解析之 GSON
  11. 基于高德地图api的热力图配置及显示调优
  12. Base64转换成图片
  13. 教师计算机考核有啥用,教师计算机使用管理制度和考核方案
  14. 计算机开启后显示器黑屏,电脑打开后显示器黑屏怎么办
  15. 人工智能--技术发展史
  16. K8S pod 时区设置
  17. 【Matlab】希腊字母
  18. TCP协议——三次握手
  19. Excel数据透视表只能求和运算?快来学习求差运算小技巧
  20. C语言过时了?扯淡!

热门文章

  1. iframe嵌套网页
  2. 干货!MySQL 资源大全
  3. 随机查询N条记录MySQL、SQLServer、Oracle、postgreSQL
  4. 十种方法保持云中数据安全
  5. 十类最让前辈讨厌的新人
  6. Chrome之排序问题
  7. 什么是泛型缓存和静态构造函数?
  8. Sentinel v1.4.2 发布,更好用的集群限流功能
  9. 设置更改root密码(远程,本地)、连接mysql、mysql常用命令
  10. iOS:使用集成的支付宝SDK的支付流程