shine_0123

shine_0123 查看完整档案

深圳编辑  |  填写毕业院校  |  填写所在公司/组织 www.cnblogs.com/lswit/ 编辑
编辑

有了

个人动态

shine_0123 收藏了文章 · 2019-03-19

deno 如何偿还 node.js 的十大技术债

根据网络资料整理

“Node现在太难用了!”。Node.js之父 Ryan Dahl 去年初要开发一款 JavaScript 互动式数据分析工具时,忍不住抱怨起自己十年前一手创造的技术。

Ryan Dahl 想要设计出一款类似 IPython 的互动式数据科学分析命令行工具,但改用 JavaScript 语言,要让 JavaScript 也可以像 Python 那样,进行各式各样的数据分析、统计计算以及数据视觉化战士。一度离开 Node.js 开发社区的 Rayn Dahl,再次拿起自己发明的 Node.js 来开发这个新的数据分析工具,但是越用越别扭,他开始思考,有什么方法可以改进 Node.js。

Node.js 是他在 2009 年 11 月 8 日时,在 JavaScript 社区欧洲 JSConf 大会上首度发布的,它把浏览器端的 JavaScript 技术,带入了服务器端应用领域。前端网页工程师从来都没想过,自己也可以成为后端工程师,但 Node.js 让前端技术走出了浏览器,前端工程师甚至可以成为全端工程师,Node.js 改变了前端工程师的世界。

从2009年 Ryan Dahl 设计出这个服务器端的 JavaScript 框架,至今已经发展到了第 10 版。而随 Node.js 而生,另一位开发者 Isaac 设计出的 JavaScript 包管理工具 npm,更成了网页开发者必懂得技术,在 npm 的储存库上,注册了超过 60 万个 Node.js 模块,这更让 Node.js 的应用遍及各类开发或软件需求。

JavaScript是最普及的语言,而Node.js是最受欢迎的框架

根据 Stack OverFlow 在 2018年度的开发者大调查(全球超过 10 万开发者参与),JavaScript 是开发者中最普及的技术,近 7 成开发者都会用,比 HTML 或 CSS 的普及率还要要高,而最多人懂的开发框架排名中,第一名就是 Node.js ,将近5成开发者(49.6%)经常使用,比 2017 年还小幅上升了 2 个百分点,同时使用者还在持续增加,远高于排名第二的 Angular(36.9%),这正是因为 Node.js 是前端和后端工程师都能用的技术。

Node.js 不只是当前的主流技术,也是下一代网页应用架构 Serverless(无服务器)架构的关键技术。负责 Azure Functions 项目的微软资深首席软件工程师 Christopher Anderson 就曾直言,主流无服务器服务商纷纷把宝押在 Node.js,因为看上了 JavaScript 工具的丰富生态,再加上 Node.js 的轻量化、容易分散与水平扩充、各种操作系统都容易运行的特性,将 Node.js 作为无服务器服务优先支持的框架,这也让 Node.js 更适合用于超大规模部署的应用。

Ryan Dahl 自己坦言,从没想到 Node.js 日后会带来这么大的影响。他也将此归功于开发社区的持续改善,才让它越来越成熟。截止到2018年8月,参与 Node.js 的开发者已经超过2千人,十年来的更新发布次数超过 500 次,在 GitHub 上代码的下载次数更是累计超过了10亿次,就连大型科技公司如 PayPal,或顶尖科研机构 NASA 都在使用。

但 Ryan Dahl 在 2012 年开始淡出 Node.js 社区,转而进入 Go、Rust 语言社区,也重回他擅长的数学应用领域,2017 年还申请了 Google 大脑一年的进驻计划,成为 Google 大脑研究团队的一员,担任深度学习工程师,并投入图像处理技术的研究。直到 2018 年 6 月初,就在 Node.js 准备迈入第 10 年之前,JSConf 欧洲大会再次邀请Ryan Dahl 来进行开场演讲。

尽管大受欢迎,但 Node.js 仍有十大技术债

原本 Ryan Dahl 打算在 2018 年的 JSConf 演讲中分享自己这款 JavaScript 版的 IPython 互动式数据分析工具,没想到一直开发到 5 月份,这个工具都还不能用。本来要放弃这次演讲的 Ryan Dahl 念头一转,干脆把他重拾 Node.js 后发现的问题拿出来分享,这就是去年引发全球开发社区热烈讨论的那场演讲,题目是 “我在 Node .js 最后悔的 10 件事”。Ryan Dahl 在演讲中坦言,Node.js 有十大设计错误,甚至可说是他的 10 大悔恨(他刻意用Regret 这个词来形容)!

这些让 Ryan Dahl 懊悔不已的错误,包括了没采用 JavaScript 非同步处理的 Promise 对象、低估了安全性的重要性、采用 gyp 来设计 Build 系统、没有听从社区建议改用 FFI 而继续用 gyp,另外,他也觉得 Node.js 过度依赖 npm 功能(内建到 package.json 支持是个错误)、用 require("")来嵌入任意模块简直太容易了、package.json 容易造成错误的模块观念(让开发者误以为同一目录下的文件就是同一模块)、臃肿复杂的node_module设计以及开发社区抱怨已久的下载黑洞问题(下载npm得花上非常久的等待时间)、require("module")功能没有强制要求注名.js扩展名,以及无用的 index.js 设计。

2012年,Ryan Dahl 离开了Node.js社区,他事后解释,Node.js 的发展已经步入正轨,也达到他最初的目标,因而决定离开,但在2018年这场演讲中,他坦言, Node.js 还有大把问题要修复,所以现在他回来了,要来偿还当年的技术债,挽回 Node.js 的设计错误。

Ryan Dahl 的答案是打造一个全新的服务器端 JavaScript 运行环境,也就是 Deno 项目。

Deno从2018年5月在 Github 开源至今年3月,已有超过100名开发者参与,经常贡献代码的核心开发者也有5名。

让Ryan Dahl懊悔不已的 Node.js 十大技术债

  1. 没用 JavaScript 异步处理的 Promise 对象
  2. 低估了安全的重要性
  3. 使用了 gyp 来设计 Build 系统
  4. 没有听大家的建议提供 FFI 而继续用 gyp
  5. 过度依赖 npm(内建 package.json支持)
  6. 太容易造成 require("任意模块")
  7. package.json 建立了错误的模块概念(在同一目录下的文件就是同一模块)
  8. 臃肿复杂的 node_module 设计和下载黑洞(往往下载 npm 得花上非常久的时间)
  9. require("module") 时没有强制加上 .js 扩展名
  10. 无用的 index.js 设计。

Deno 如何挽回 Node.js 设计上遗留的问题

clipboard.png

这是 Deno 项目的一个范例,是 Unix 系统基本命令 cat 的一个实现。cat 可以从标准输入取得文件, 再逐行把文件内容送出到标准输出上。这个范例反映出 Deno 要将 I/O 抽象化和精简化的意图。

Ryan Dahl 希望通过打造 Deno 这个全新的服务器端 JavaScript 运行环境,来解决 Node.js 的三大问题,包括准确的 I/O 接口、预设安全性(Secure by Default)以及引进一套去中心化的模块系统等,最后一项就是要解决下载过久过慢的老问题。

Ryan Dahl 进一步解释,虽然他所有的时间都是用 C++、Go 或 Rust 这类编译式语言来开发,但是他还是有一些经常要做的事,需要使用动态的脚本程序。例如整理资料、协调测试任务、部署服务器或用户端环境、绘制统计图表、设定构建系统(Build System)的参数或是设计应用雏形等。

可是,这些不同用途的任务,需要切换使用多种不同的脚本语言或工具,如 Bash、Python 或是 Node.js 等才行,相当麻烦。而 2018 年上半年的这个互动式数据分析工具的开发挫折,更让他有一股强烈地念头,能不能有一个通用的脚本工具。

Deno

Deno 架构

打造一款简单、好用的通用脚本工具

Ryan Dahl 在 2018 年 11 月参加台湾年度 JavaScript 开发者大会(JSDC)时,特别提到:“我不喜欢用不同工具来处理不同的事情,我只想要有一个简单,直接可执行,拿了就能用的顺手工具,这正是打造Deno的初衷。”

简单来说,Deno 跟 Node.js 一样都采用了 Chrome 的 JavaScript 引擎 V8,但 Deno 采用了更严格的 JavaScript 语法规范 TypeScript,Deno 等于是一个 TypeScript runtime。第一个版本的 Deno runtime 是用 Go 语言实现的,但是 Ryan Dahl 又重新用 Rust 语言开发了一次 Deno 的 runtime,避免因为重复使用两套垃圾回收器(Go语言一套、V8引擎也内建了一套)而影响效能。另外,Deno runtime 中也内建了 TypeScript 编译器。

Deno的目标是安全、简洁、单一可执行文件

Deno的设计目标是安全、模块简洁、单一可执行文件(简化封装)等,目前已完成的特色之一,就是可以透过URL 来汇入各种模块,另外预设安全性,要存取实体资源或网络时都需要授权,用户的代码只能在安全的沙箱中执行。为什么他最熟悉的 Go 做不到?因为“动态语言仍有其必要。“他强调,尤其要建立一个适当好用的 I/O 处理流程(pipeline)时,动态脚本语言是不可或缺的工具。

而 JavaScript 就是那个他心中的理想动态语言,但是,Node.js 是一项将近 10 年历史的技术,受限于最初的设计架构,他认为,可以重新用 JavaScript 近几年出现的特性,重新思考 Node. js 的根本设计,包括像是可存取原始内存的标准方法 ArrayBuffers、适合弹性组合的 TypeScript Interfaces,以及新兴的非同步机制 Async 和 Await。Ryan Dahl 把这些新的 JavaScipt 功能,放入了 Deno 中,来设计一款新的服务器端 JavaScript 框架。

但是,这一次,他不想重走 Node.js 的老路,将整个 Web 服务器放进框架,Ryan Dahl 决定打造出一支自给自足功能完整的 runtime 程序,容易带着走,而不是有着一套复杂目录和结构的框架。

而且,打包成 runtime 形式,就可以部署到各种环境中,Ryan Dahl 举例,如果在无服务器服务上部署了Deno,就可指定一个网址,就能启动这个无服务器服务的调用,而不用上传一段代码到无服务器服务平台上执行,也可以部署到边缘运算设备中,来完成小型的数据处理工作。

不信任使用者的代码,只能在沙箱执行

另外在安全机制上,Deno 设计了两层权限架构,一个是拥有特权的核心层,另一个是没有特权的用户空间,RyanDahl 解释到,这就像是操作系统的设计一样,不信任使用者的代码,区分出使用者的权限和系统核心的权限等级,使用者的程序,要向使用到关键的资源,必须透过系统调用,由系统核心程序来执行。Deno 也是一样,“不信任用户端的 JavaScript 程序,只能在安全的沙箱中执行。”

所有涉及到敏感资源的处理,如底层文件系统,都需要授权执行,这就是预设安全性的设计,包括网络存取、文件系统写入、环境变量存取、执行等这些敏感的动作,都需要取得授权才能执行。

另外,Deno 的设计还将 I/O 处理抽象化,让 JavaScript 程序不必处理各种不同的输出或输入端配置,改由 runtime 接手,从而无法直接接触实体资源。一来简化各种不同的 I/O 存取方式,不论是本地或远 I/O,都是同样的 read 和 write 指令就可以搞定,二来也可以强化安全性。

另外,Deno 还借鉴了不少 Go 语言的特性,例如 Deno 的 copy() 就参考了 Go 语言的 io.Copy()BufReader() 也参考了 Go 语言的 bufio.Reader 设计等,还有不少测试机制、文件等,Ryan Dahl 也都参考了他熟悉的Go语言。

指定URL,就能嵌入第三方函数库

Deno 第三项设计目标是要打造一个去中心化的模块系统,Ryan Dahl 的设计是,Deno 可以像 JavaScript 一样,通过 URL 网址来嵌入外部的第三方函数库。除此之外,Deno和Rust语言也有不少整合,甚至改用Rust来实作Deno之后,Ryan Dahl透露,将会建立一个桥接机制,让Deno可以很容易地运用Rust中的函数库。

不过他坦言,嵌入外部的第三方函数库的机制,也是 Deno 项目发布后,人们询问最多、也最担心的功能,担心透过 URL 来引入外部函数库后,容易引发安全问题或是中间人攻击等问题。“这就是为什么 Deno 要采取预设安全性的设计的原因,来隔绝来自外部第三方代码的威胁。”

另外,通过 URL 连结到第三方函数库时,Deno 会通过缓存,在本地环境生成一份第三方函数库,第二次再调用到同样的URL时就不需再次下载了,以此来加快执行速度。甚至这个通过外部 URL 来引用函数库的功能,还可以指定版本,就算是改版了,还是可以指向旧版。当然 Ryan Dahl 强调,所有存取外部网络或下载写入到本地文件的动作,都需要取得授权才能执行。

未来会支持 WebGL,Deno 就能调用 GPU 资源

Deno 还有一个与 Node.js 最大的差异,就是未来会支持机器学习。Ryan Dahl 透露,他们正在开发一个数值计算类的外挂模块,最重要的就是要能支持 GPU,这也是机器学习模块最需要的功能。“Deno未来将会原生支持 WebGL,就可以让 JavaScipt 程序调用 GPU 的资源。这是他打造 Deno 的目标之一。”甚至,Ryan Dahl 预告,未来说不定可以把 TensorFlow JS 放上 Deno 来执行,因为 TensorFlow JS 用的也是 WebGL。

Deno未来将瞄准小型机器学习的推理需求

不同于 Nvidia的CUDA 可以用来调度多颗 GPU 资源进行复杂的机器学习训练工作,Ryan Dahl 解释,Deno 想要提供的是简单够用的机器学习能力,可以用来满足只有单颗 GPU,而且是小型或是只需要推理的计算需求,支持WebGL 已经够用了。

Deno 从 2018 年 5 月中放上 Github 网站开源至今年3月,已有超过80名开发者参与,经常贡献代码的核心开发者也有 5 名。目前正把主要精力放在在预设安全性架构的设计功能上。

最后企业能不能用 Deno?Ryan Dahl 坦言 Deno 距离 1.0 还有很长一段路要走,仍旧是一个非常新的技术。不过,“再等我1年,若有企业想用,请Email给我,我会提供技术支持。” 他认真地说。

关于Ryan Dahl

clipboard.png

2009年11月8日,Node. js之 父RyanDahl在欧洲 JSConf 大会,第一次发布了 Node.js ,一鸣惊人,将浏览器端的 JavaScript 技术,带入了服务器端应用领域。不过他从 2012 年开始淡出 Node.js 社区,转而进入Go、Rust语言社区,2017年加入 Google 大脑研究团队,担任深度学习工程师。现为自由开发者。

2018年6月初,Ryan Dahl 在 JSConf 欧洲大会发表了 Node.js 十大悔恨,并推出了新的服务器端 JavaScript runtime 方案 Deno。


本文首发微信公众号:jingchengyideng

欢迎扫描二维码关注公众号,每天都给你推送新鲜的前端技术文章

欢迎扫描二维码关注公众号,每天都给你推送新鲜的前端技术文章



欢迎继续阅读本专栏其它高赞文章:

查看原文

shine_0123 赞了文章 · 2019-03-19

deno 如何偿还 node.js 的十大技术债

根据网络资料整理

“Node现在太难用了!”。Node.js之父 Ryan Dahl 去年初要开发一款 JavaScript 互动式数据分析工具时,忍不住抱怨起自己十年前一手创造的技术。

Ryan Dahl 想要设计出一款类似 IPython 的互动式数据科学分析命令行工具,但改用 JavaScript 语言,要让 JavaScript 也可以像 Python 那样,进行各式各样的数据分析、统计计算以及数据视觉化战士。一度离开 Node.js 开发社区的 Rayn Dahl,再次拿起自己发明的 Node.js 来开发这个新的数据分析工具,但是越用越别扭,他开始思考,有什么方法可以改进 Node.js。

Node.js 是他在 2009 年 11 月 8 日时,在 JavaScript 社区欧洲 JSConf 大会上首度发布的,它把浏览器端的 JavaScript 技术,带入了服务器端应用领域。前端网页工程师从来都没想过,自己也可以成为后端工程师,但 Node.js 让前端技术走出了浏览器,前端工程师甚至可以成为全端工程师,Node.js 改变了前端工程师的世界。

从2009年 Ryan Dahl 设计出这个服务器端的 JavaScript 框架,至今已经发展到了第 10 版。而随 Node.js 而生,另一位开发者 Isaac 设计出的 JavaScript 包管理工具 npm,更成了网页开发者必懂得技术,在 npm 的储存库上,注册了超过 60 万个 Node.js 模块,这更让 Node.js 的应用遍及各类开发或软件需求。

JavaScript是最普及的语言,而Node.js是最受欢迎的框架

根据 Stack OverFlow 在 2018年度的开发者大调查(全球超过 10 万开发者参与),JavaScript 是开发者中最普及的技术,近 7 成开发者都会用,比 HTML 或 CSS 的普及率还要要高,而最多人懂的开发框架排名中,第一名就是 Node.js ,将近5成开发者(49.6%)经常使用,比 2017 年还小幅上升了 2 个百分点,同时使用者还在持续增加,远高于排名第二的 Angular(36.9%),这正是因为 Node.js 是前端和后端工程师都能用的技术。

Node.js 不只是当前的主流技术,也是下一代网页应用架构 Serverless(无服务器)架构的关键技术。负责 Azure Functions 项目的微软资深首席软件工程师 Christopher Anderson 就曾直言,主流无服务器服务商纷纷把宝押在 Node.js,因为看上了 JavaScript 工具的丰富生态,再加上 Node.js 的轻量化、容易分散与水平扩充、各种操作系统都容易运行的特性,将 Node.js 作为无服务器服务优先支持的框架,这也让 Node.js 更适合用于超大规模部署的应用。

Ryan Dahl 自己坦言,从没想到 Node.js 日后会带来这么大的影响。他也将此归功于开发社区的持续改善,才让它越来越成熟。截止到2018年8月,参与 Node.js 的开发者已经超过2千人,十年来的更新发布次数超过 500 次,在 GitHub 上代码的下载次数更是累计超过了10亿次,就连大型科技公司如 PayPal,或顶尖科研机构 NASA 都在使用。

但 Ryan Dahl 在 2012 年开始淡出 Node.js 社区,转而进入 Go、Rust 语言社区,也重回他擅长的数学应用领域,2017 年还申请了 Google 大脑一年的进驻计划,成为 Google 大脑研究团队的一员,担任深度学习工程师,并投入图像处理技术的研究。直到 2018 年 6 月初,就在 Node.js 准备迈入第 10 年之前,JSConf 欧洲大会再次邀请Ryan Dahl 来进行开场演讲。

尽管大受欢迎,但 Node.js 仍有十大技术债

原本 Ryan Dahl 打算在 2018 年的 JSConf 演讲中分享自己这款 JavaScript 版的 IPython 互动式数据分析工具,没想到一直开发到 5 月份,这个工具都还不能用。本来要放弃这次演讲的 Ryan Dahl 念头一转,干脆把他重拾 Node.js 后发现的问题拿出来分享,这就是去年引发全球开发社区热烈讨论的那场演讲,题目是 “我在 Node .js 最后悔的 10 件事”。Ryan Dahl 在演讲中坦言,Node.js 有十大设计错误,甚至可说是他的 10 大悔恨(他刻意用Regret 这个词来形容)!

这些让 Ryan Dahl 懊悔不已的错误,包括了没采用 JavaScript 非同步处理的 Promise 对象、低估了安全性的重要性、采用 gyp 来设计 Build 系统、没有听从社区建议改用 FFI 而继续用 gyp,另外,他也觉得 Node.js 过度依赖 npm 功能(内建到 package.json 支持是个错误)、用 require("")来嵌入任意模块简直太容易了、package.json 容易造成错误的模块观念(让开发者误以为同一目录下的文件就是同一模块)、臃肿复杂的node_module设计以及开发社区抱怨已久的下载黑洞问题(下载npm得花上非常久的等待时间)、require("module")功能没有强制要求注名.js扩展名,以及无用的 index.js 设计。

2012年,Ryan Dahl 离开了Node.js社区,他事后解释,Node.js 的发展已经步入正轨,也达到他最初的目标,因而决定离开,但在2018年这场演讲中,他坦言, Node.js 还有大把问题要修复,所以现在他回来了,要来偿还当年的技术债,挽回 Node.js 的设计错误。

Ryan Dahl 的答案是打造一个全新的服务器端 JavaScript 运行环境,也就是 Deno 项目。

Deno从2018年5月在 Github 开源至今年3月,已有超过100名开发者参与,经常贡献代码的核心开发者也有5名。

让Ryan Dahl懊悔不已的 Node.js 十大技术债

  1. 没用 JavaScript 异步处理的 Promise 对象
  2. 低估了安全的重要性
  3. 使用了 gyp 来设计 Build 系统
  4. 没有听大家的建议提供 FFI 而继续用 gyp
  5. 过度依赖 npm(内建 package.json支持)
  6. 太容易造成 require("任意模块")
  7. package.json 建立了错误的模块概念(在同一目录下的文件就是同一模块)
  8. 臃肿复杂的 node_module 设计和下载黑洞(往往下载 npm 得花上非常久的时间)
  9. require("module") 时没有强制加上 .js 扩展名
  10. 无用的 index.js 设计。

Deno 如何挽回 Node.js 设计上遗留的问题

clipboard.png

这是 Deno 项目的一个范例,是 Unix 系统基本命令 cat 的一个实现。cat 可以从标准输入取得文件, 再逐行把文件内容送出到标准输出上。这个范例反映出 Deno 要将 I/O 抽象化和精简化的意图。

Ryan Dahl 希望通过打造 Deno 这个全新的服务器端 JavaScript 运行环境,来解决 Node.js 的三大问题,包括准确的 I/O 接口、预设安全性(Secure by Default)以及引进一套去中心化的模块系统等,最后一项就是要解决下载过久过慢的老问题。

Ryan Dahl 进一步解释,虽然他所有的时间都是用 C++、Go 或 Rust 这类编译式语言来开发,但是他还是有一些经常要做的事,需要使用动态的脚本程序。例如整理资料、协调测试任务、部署服务器或用户端环境、绘制统计图表、设定构建系统(Build System)的参数或是设计应用雏形等。

可是,这些不同用途的任务,需要切换使用多种不同的脚本语言或工具,如 Bash、Python 或是 Node.js 等才行,相当麻烦。而 2018 年上半年的这个互动式数据分析工具的开发挫折,更让他有一股强烈地念头,能不能有一个通用的脚本工具。

Deno

Deno 架构

打造一款简单、好用的通用脚本工具

Ryan Dahl 在 2018 年 11 月参加台湾年度 JavaScript 开发者大会(JSDC)时,特别提到:“我不喜欢用不同工具来处理不同的事情,我只想要有一个简单,直接可执行,拿了就能用的顺手工具,这正是打造Deno的初衷。”

简单来说,Deno 跟 Node.js 一样都采用了 Chrome 的 JavaScript 引擎 V8,但 Deno 采用了更严格的 JavaScript 语法规范 TypeScript,Deno 等于是一个 TypeScript runtime。第一个版本的 Deno runtime 是用 Go 语言实现的,但是 Ryan Dahl 又重新用 Rust 语言开发了一次 Deno 的 runtime,避免因为重复使用两套垃圾回收器(Go语言一套、V8引擎也内建了一套)而影响效能。另外,Deno runtime 中也内建了 TypeScript 编译器。

Deno的目标是安全、简洁、单一可执行文件

Deno的设计目标是安全、模块简洁、单一可执行文件(简化封装)等,目前已完成的特色之一,就是可以透过URL 来汇入各种模块,另外预设安全性,要存取实体资源或网络时都需要授权,用户的代码只能在安全的沙箱中执行。为什么他最熟悉的 Go 做不到?因为“动态语言仍有其必要。“他强调,尤其要建立一个适当好用的 I/O 处理流程(pipeline)时,动态脚本语言是不可或缺的工具。

而 JavaScript 就是那个他心中的理想动态语言,但是,Node.js 是一项将近 10 年历史的技术,受限于最初的设计架构,他认为,可以重新用 JavaScript 近几年出现的特性,重新思考 Node. js 的根本设计,包括像是可存取原始内存的标准方法 ArrayBuffers、适合弹性组合的 TypeScript Interfaces,以及新兴的非同步机制 Async 和 Await。Ryan Dahl 把这些新的 JavaScipt 功能,放入了 Deno 中,来设计一款新的服务器端 JavaScript 框架。

但是,这一次,他不想重走 Node.js 的老路,将整个 Web 服务器放进框架,Ryan Dahl 决定打造出一支自给自足功能完整的 runtime 程序,容易带着走,而不是有着一套复杂目录和结构的框架。

而且,打包成 runtime 形式,就可以部署到各种环境中,Ryan Dahl 举例,如果在无服务器服务上部署了Deno,就可指定一个网址,就能启动这个无服务器服务的调用,而不用上传一段代码到无服务器服务平台上执行,也可以部署到边缘运算设备中,来完成小型的数据处理工作。

不信任使用者的代码,只能在沙箱执行

另外在安全机制上,Deno 设计了两层权限架构,一个是拥有特权的核心层,另一个是没有特权的用户空间,RyanDahl 解释到,这就像是操作系统的设计一样,不信任使用者的代码,区分出使用者的权限和系统核心的权限等级,使用者的程序,要向使用到关键的资源,必须透过系统调用,由系统核心程序来执行。Deno 也是一样,“不信任用户端的 JavaScript 程序,只能在安全的沙箱中执行。”

所有涉及到敏感资源的处理,如底层文件系统,都需要授权执行,这就是预设安全性的设计,包括网络存取、文件系统写入、环境变量存取、执行等这些敏感的动作,都需要取得授权才能执行。

另外,Deno 的设计还将 I/O 处理抽象化,让 JavaScript 程序不必处理各种不同的输出或输入端配置,改由 runtime 接手,从而无法直接接触实体资源。一来简化各种不同的 I/O 存取方式,不论是本地或远 I/O,都是同样的 read 和 write 指令就可以搞定,二来也可以强化安全性。

另外,Deno 还借鉴了不少 Go 语言的特性,例如 Deno 的 copy() 就参考了 Go 语言的 io.Copy()BufReader() 也参考了 Go 语言的 bufio.Reader 设计等,还有不少测试机制、文件等,Ryan Dahl 也都参考了他熟悉的Go语言。

指定URL,就能嵌入第三方函数库

Deno 第三项设计目标是要打造一个去中心化的模块系统,Ryan Dahl 的设计是,Deno 可以像 JavaScript 一样,通过 URL 网址来嵌入外部的第三方函数库。除此之外,Deno和Rust语言也有不少整合,甚至改用Rust来实作Deno之后,Ryan Dahl透露,将会建立一个桥接机制,让Deno可以很容易地运用Rust中的函数库。

不过他坦言,嵌入外部的第三方函数库的机制,也是 Deno 项目发布后,人们询问最多、也最担心的功能,担心透过 URL 来引入外部函数库后,容易引发安全问题或是中间人攻击等问题。“这就是为什么 Deno 要采取预设安全性的设计的原因,来隔绝来自外部第三方代码的威胁。”

另外,通过 URL 连结到第三方函数库时,Deno 会通过缓存,在本地环境生成一份第三方函数库,第二次再调用到同样的URL时就不需再次下载了,以此来加快执行速度。甚至这个通过外部 URL 来引用函数库的功能,还可以指定版本,就算是改版了,还是可以指向旧版。当然 Ryan Dahl 强调,所有存取外部网络或下载写入到本地文件的动作,都需要取得授权才能执行。

未来会支持 WebGL,Deno 就能调用 GPU 资源

Deno 还有一个与 Node.js 最大的差异,就是未来会支持机器学习。Ryan Dahl 透露,他们正在开发一个数值计算类的外挂模块,最重要的就是要能支持 GPU,这也是机器学习模块最需要的功能。“Deno未来将会原生支持 WebGL,就可以让 JavaScipt 程序调用 GPU 的资源。这是他打造 Deno 的目标之一。”甚至,Ryan Dahl 预告,未来说不定可以把 TensorFlow JS 放上 Deno 来执行,因为 TensorFlow JS 用的也是 WebGL。

Deno未来将瞄准小型机器学习的推理需求

不同于 Nvidia的CUDA 可以用来调度多颗 GPU 资源进行复杂的机器学习训练工作,Ryan Dahl 解释,Deno 想要提供的是简单够用的机器学习能力,可以用来满足只有单颗 GPU,而且是小型或是只需要推理的计算需求,支持WebGL 已经够用了。

Deno 从 2018 年 5 月中放上 Github 网站开源至今年3月,已有超过80名开发者参与,经常贡献代码的核心开发者也有 5 名。目前正把主要精力放在在预设安全性架构的设计功能上。

最后企业能不能用 Deno?Ryan Dahl 坦言 Deno 距离 1.0 还有很长一段路要走,仍旧是一个非常新的技术。不过,“再等我1年,若有企业想用,请Email给我,我会提供技术支持。” 他认真地说。

关于Ryan Dahl

clipboard.png

2009年11月8日,Node. js之 父RyanDahl在欧洲 JSConf 大会,第一次发布了 Node.js ,一鸣惊人,将浏览器端的 JavaScript 技术,带入了服务器端应用领域。不过他从 2012 年开始淡出 Node.js 社区,转而进入Go、Rust语言社区,2017年加入 Google 大脑研究团队,担任深度学习工程师。现为自由开发者。

2018年6月初,Ryan Dahl 在 JSConf 欧洲大会发表了 Node.js 十大悔恨,并推出了新的服务器端 JavaScript runtime 方案 Deno。


本文首发微信公众号:jingchengyideng

欢迎扫描二维码关注公众号,每天都给你推送新鲜的前端技术文章

欢迎扫描二维码关注公众号,每天都给你推送新鲜的前端技术文章



欢迎继续阅读本专栏其它高赞文章:

查看原文

赞 29 收藏 19 评论 3

shine_0123 评论了文章 · 2019-03-19

deno 如何偿还 node.js 的十大技术债

根据网络资料整理

“Node现在太难用了!”。Node.js之父 Ryan Dahl 去年初要开发一款 JavaScript 互动式数据分析工具时,忍不住抱怨起自己十年前一手创造的技术。

Ryan Dahl 想要设计出一款类似 IPython 的互动式数据科学分析命令行工具,但改用 JavaScript 语言,要让 JavaScript 也可以像 Python 那样,进行各式各样的数据分析、统计计算以及数据视觉化战士。一度离开 Node.js 开发社区的 Rayn Dahl,再次拿起自己发明的 Node.js 来开发这个新的数据分析工具,但是越用越别扭,他开始思考,有什么方法可以改进 Node.js。

Node.js 是他在 2009 年 11 月 8 日时,在 JavaScript 社区欧洲 JSConf 大会上首度发布的,它把浏览器端的 JavaScript 技术,带入了服务器端应用领域。前端网页工程师从来都没想过,自己也可以成为后端工程师,但 Node.js 让前端技术走出了浏览器,前端工程师甚至可以成为全端工程师,Node.js 改变了前端工程师的世界。

从2009年 Ryan Dahl 设计出这个服务器端的 JavaScript 框架,至今已经发展到了第 10 版。而随 Node.js 而生,另一位开发者 Isaac 设计出的 JavaScript 包管理工具 npm,更成了网页开发者必懂得技术,在 npm 的储存库上,注册了超过 60 万个 Node.js 模块,这更让 Node.js 的应用遍及各类开发或软件需求。

JavaScript是最普及的语言,而Node.js是最受欢迎的框架

根据 Stack OverFlow 在 2018年度的开发者大调查(全球超过 10 万开发者参与),JavaScript 是开发者中最普及的技术,近 7 成开发者都会用,比 HTML 或 CSS 的普及率还要要高,而最多人懂的开发框架排名中,第一名就是 Node.js ,将近5成开发者(49.6%)经常使用,比 2017 年还小幅上升了 2 个百分点,同时使用者还在持续增加,远高于排名第二的 Angular(36.9%),这正是因为 Node.js 是前端和后端工程师都能用的技术。

Node.js 不只是当前的主流技术,也是下一代网页应用架构 Serverless(无服务器)架构的关键技术。负责 Azure Functions 项目的微软资深首席软件工程师 Christopher Anderson 就曾直言,主流无服务器服务商纷纷把宝押在 Node.js,因为看上了 JavaScript 工具的丰富生态,再加上 Node.js 的轻量化、容易分散与水平扩充、各种操作系统都容易运行的特性,将 Node.js 作为无服务器服务优先支持的框架,这也让 Node.js 更适合用于超大规模部署的应用。

Ryan Dahl 自己坦言,从没想到 Node.js 日后会带来这么大的影响。他也将此归功于开发社区的持续改善,才让它越来越成熟。截止到2018年8月,参与 Node.js 的开发者已经超过2千人,十年来的更新发布次数超过 500 次,在 GitHub 上代码的下载次数更是累计超过了10亿次,就连大型科技公司如 PayPal,或顶尖科研机构 NASA 都在使用。

但 Ryan Dahl 在 2012 年开始淡出 Node.js 社区,转而进入 Go、Rust 语言社区,也重回他擅长的数学应用领域,2017 年还申请了 Google 大脑一年的进驻计划,成为 Google 大脑研究团队的一员,担任深度学习工程师,并投入图像处理技术的研究。直到 2018 年 6 月初,就在 Node.js 准备迈入第 10 年之前,JSConf 欧洲大会再次邀请Ryan Dahl 来进行开场演讲。

尽管大受欢迎,但 Node.js 仍有十大技术债

原本 Ryan Dahl 打算在 2018 年的 JSConf 演讲中分享自己这款 JavaScript 版的 IPython 互动式数据分析工具,没想到一直开发到 5 月份,这个工具都还不能用。本来要放弃这次演讲的 Ryan Dahl 念头一转,干脆把他重拾 Node.js 后发现的问题拿出来分享,这就是去年引发全球开发社区热烈讨论的那场演讲,题目是 “我在 Node .js 最后悔的 10 件事”。Ryan Dahl 在演讲中坦言,Node.js 有十大设计错误,甚至可说是他的 10 大悔恨(他刻意用Regret 这个词来形容)!

这些让 Ryan Dahl 懊悔不已的错误,包括了没采用 JavaScript 非同步处理的 Promise 对象、低估了安全性的重要性、采用 gyp 来设计 Build 系统、没有听从社区建议改用 FFI 而继续用 gyp,另外,他也觉得 Node.js 过度依赖 npm 功能(内建到 package.json 支持是个错误)、用 require("")来嵌入任意模块简直太容易了、package.json 容易造成错误的模块观念(让开发者误以为同一目录下的文件就是同一模块)、臃肿复杂的node_module设计以及开发社区抱怨已久的下载黑洞问题(下载npm得花上非常久的等待时间)、require("module")功能没有强制要求注名.js扩展名,以及无用的 index.js 设计。

2012年,Ryan Dahl 离开了Node.js社区,他事后解释,Node.js 的发展已经步入正轨,也达到他最初的目标,因而决定离开,但在2018年这场演讲中,他坦言, Node.js 还有大把问题要修复,所以现在他回来了,要来偿还当年的技术债,挽回 Node.js 的设计错误。

Ryan Dahl 的答案是打造一个全新的服务器端 JavaScript 运行环境,也就是 Deno 项目。

Deno从2018年5月在 Github 开源至今年3月,已有超过100名开发者参与,经常贡献代码的核心开发者也有5名。

让Ryan Dahl懊悔不已的 Node.js 十大技术债

  1. 没用 JavaScript 异步处理的 Promise 对象
  2. 低估了安全的重要性
  3. 使用了 gyp 来设计 Build 系统
  4. 没有听大家的建议提供 FFI 而继续用 gyp
  5. 过度依赖 npm(内建 package.json支持)
  6. 太容易造成 require("任意模块")
  7. package.json 建立了错误的模块概念(在同一目录下的文件就是同一模块)
  8. 臃肿复杂的 node_module 设计和下载黑洞(往往下载 npm 得花上非常久的时间)
  9. require("module") 时没有强制加上 .js 扩展名
  10. 无用的 index.js 设计。

Deno 如何挽回 Node.js 设计上遗留的问题

clipboard.png

这是 Deno 项目的一个范例,是 Unix 系统基本命令 cat 的一个实现。cat 可以从标准输入取得文件, 再逐行把文件内容送出到标准输出上。这个范例反映出 Deno 要将 I/O 抽象化和精简化的意图。

Ryan Dahl 希望通过打造 Deno 这个全新的服务器端 JavaScript 运行环境,来解决 Node.js 的三大问题,包括准确的 I/O 接口、预设安全性(Secure by Default)以及引进一套去中心化的模块系统等,最后一项就是要解决下载过久过慢的老问题。

Ryan Dahl 进一步解释,虽然他所有的时间都是用 C++、Go 或 Rust 这类编译式语言来开发,但是他还是有一些经常要做的事,需要使用动态的脚本程序。例如整理资料、协调测试任务、部署服务器或用户端环境、绘制统计图表、设定构建系统(Build System)的参数或是设计应用雏形等。

可是,这些不同用途的任务,需要切换使用多种不同的脚本语言或工具,如 Bash、Python 或是 Node.js 等才行,相当麻烦。而 2018 年上半年的这个互动式数据分析工具的开发挫折,更让他有一股强烈地念头,能不能有一个通用的脚本工具。

Deno

Deno 架构

打造一款简单、好用的通用脚本工具

Ryan Dahl 在 2018 年 11 月参加台湾年度 JavaScript 开发者大会(JSDC)时,特别提到:“我不喜欢用不同工具来处理不同的事情,我只想要有一个简单,直接可执行,拿了就能用的顺手工具,这正是打造Deno的初衷。”

简单来说,Deno 跟 Node.js 一样都采用了 Chrome 的 JavaScript 引擎 V8,但 Deno 采用了更严格的 JavaScript 语法规范 TypeScript,Deno 等于是一个 TypeScript runtime。第一个版本的 Deno runtime 是用 Go 语言实现的,但是 Ryan Dahl 又重新用 Rust 语言开发了一次 Deno 的 runtime,避免因为重复使用两套垃圾回收器(Go语言一套、V8引擎也内建了一套)而影响效能。另外,Deno runtime 中也内建了 TypeScript 编译器。

Deno的目标是安全、简洁、单一可执行文件

Deno的设计目标是安全、模块简洁、单一可执行文件(简化封装)等,目前已完成的特色之一,就是可以透过URL 来汇入各种模块,另外预设安全性,要存取实体资源或网络时都需要授权,用户的代码只能在安全的沙箱中执行。为什么他最熟悉的 Go 做不到?因为“动态语言仍有其必要。“他强调,尤其要建立一个适当好用的 I/O 处理流程(pipeline)时,动态脚本语言是不可或缺的工具。

而 JavaScript 就是那个他心中的理想动态语言,但是,Node.js 是一项将近 10 年历史的技术,受限于最初的设计架构,他认为,可以重新用 JavaScript 近几年出现的特性,重新思考 Node. js 的根本设计,包括像是可存取原始内存的标准方法 ArrayBuffers、适合弹性组合的 TypeScript Interfaces,以及新兴的非同步机制 Async 和 Await。Ryan Dahl 把这些新的 JavaScipt 功能,放入了 Deno 中,来设计一款新的服务器端 JavaScript 框架。

但是,这一次,他不想重走 Node.js 的老路,将整个 Web 服务器放进框架,Ryan Dahl 决定打造出一支自给自足功能完整的 runtime 程序,容易带着走,而不是有着一套复杂目录和结构的框架。

而且,打包成 runtime 形式,就可以部署到各种环境中,Ryan Dahl 举例,如果在无服务器服务上部署了Deno,就可指定一个网址,就能启动这个无服务器服务的调用,而不用上传一段代码到无服务器服务平台上执行,也可以部署到边缘运算设备中,来完成小型的数据处理工作。

不信任使用者的代码,只能在沙箱执行

另外在安全机制上,Deno 设计了两层权限架构,一个是拥有特权的核心层,另一个是没有特权的用户空间,RyanDahl 解释到,这就像是操作系统的设计一样,不信任使用者的代码,区分出使用者的权限和系统核心的权限等级,使用者的程序,要向使用到关键的资源,必须透过系统调用,由系统核心程序来执行。Deno 也是一样,“不信任用户端的 JavaScript 程序,只能在安全的沙箱中执行。”

所有涉及到敏感资源的处理,如底层文件系统,都需要授权执行,这就是预设安全性的设计,包括网络存取、文件系统写入、环境变量存取、执行等这些敏感的动作,都需要取得授权才能执行。

另外,Deno 的设计还将 I/O 处理抽象化,让 JavaScript 程序不必处理各种不同的输出或输入端配置,改由 runtime 接手,从而无法直接接触实体资源。一来简化各种不同的 I/O 存取方式,不论是本地或远 I/O,都是同样的 read 和 write 指令就可以搞定,二来也可以强化安全性。

另外,Deno 还借鉴了不少 Go 语言的特性,例如 Deno 的 copy() 就参考了 Go 语言的 io.Copy()BufReader() 也参考了 Go 语言的 bufio.Reader 设计等,还有不少测试机制、文件等,Ryan Dahl 也都参考了他熟悉的Go语言。

指定URL,就能嵌入第三方函数库

Deno 第三项设计目标是要打造一个去中心化的模块系统,Ryan Dahl 的设计是,Deno 可以像 JavaScript 一样,通过 URL 网址来嵌入外部的第三方函数库。除此之外,Deno和Rust语言也有不少整合,甚至改用Rust来实作Deno之后,Ryan Dahl透露,将会建立一个桥接机制,让Deno可以很容易地运用Rust中的函数库。

不过他坦言,嵌入外部的第三方函数库的机制,也是 Deno 项目发布后,人们询问最多、也最担心的功能,担心透过 URL 来引入外部函数库后,容易引发安全问题或是中间人攻击等问题。“这就是为什么 Deno 要采取预设安全性的设计的原因,来隔绝来自外部第三方代码的威胁。”

另外,通过 URL 连结到第三方函数库时,Deno 会通过缓存,在本地环境生成一份第三方函数库,第二次再调用到同样的URL时就不需再次下载了,以此来加快执行速度。甚至这个通过外部 URL 来引用函数库的功能,还可以指定版本,就算是改版了,还是可以指向旧版。当然 Ryan Dahl 强调,所有存取外部网络或下载写入到本地文件的动作,都需要取得授权才能执行。

未来会支持 WebGL,Deno 就能调用 GPU 资源

Deno 还有一个与 Node.js 最大的差异,就是未来会支持机器学习。Ryan Dahl 透露,他们正在开发一个数值计算类的外挂模块,最重要的就是要能支持 GPU,这也是机器学习模块最需要的功能。“Deno未来将会原生支持 WebGL,就可以让 JavaScipt 程序调用 GPU 的资源。这是他打造 Deno 的目标之一。”甚至,Ryan Dahl 预告,未来说不定可以把 TensorFlow JS 放上 Deno 来执行,因为 TensorFlow JS 用的也是 WebGL。

Deno未来将瞄准小型机器学习的推理需求

不同于 Nvidia的CUDA 可以用来调度多颗 GPU 资源进行复杂的机器学习训练工作,Ryan Dahl 解释,Deno 想要提供的是简单够用的机器学习能力,可以用来满足只有单颗 GPU,而且是小型或是只需要推理的计算需求,支持WebGL 已经够用了。

Deno 从 2018 年 5 月中放上 Github 网站开源至今年3月,已有超过80名开发者参与,经常贡献代码的核心开发者也有 5 名。目前正把主要精力放在在预设安全性架构的设计功能上。

最后企业能不能用 Deno?Ryan Dahl 坦言 Deno 距离 1.0 还有很长一段路要走,仍旧是一个非常新的技术。不过,“再等我1年,若有企业想用,请Email给我,我会提供技术支持。” 他认真地说。

关于Ryan Dahl

clipboard.png

2009年11月8日,Node. js之 父RyanDahl在欧洲 JSConf 大会,第一次发布了 Node.js ,一鸣惊人,将浏览器端的 JavaScript 技术,带入了服务器端应用领域。不过他从 2012 年开始淡出 Node.js 社区,转而进入Go、Rust语言社区,2017年加入 Google 大脑研究团队,担任深度学习工程师。现为自由开发者。

2018年6月初,Ryan Dahl 在 JSConf 欧洲大会发表了 Node.js 十大悔恨,并推出了新的服务器端 JavaScript runtime 方案 Deno。


本文首发微信公众号:jingchengyideng

欢迎扫描二维码关注公众号,每天都给你推送新鲜的前端技术文章

欢迎扫描二维码关注公众号,每天都给你推送新鲜的前端技术文章



欢迎继续阅读本专栏其它高赞文章:

查看原文

shine_0123 赞了文章 · 2019-02-21

【译】什么是Node.js以及我们为什么要使用它?

Node.js是一个事件驱动且具有非阻塞I/O的JavaScript环境,这使其轻量又高效。与初始的浏览器相比,它是一种具有多种功能的广泛的函数式编程语言。事实上,行业专家通过一个短语来简单定义它,“一个高科技开源引擎,其中包含数百万由Node.js开发者编写的软件包”


为什么选择Node.js


Node.js是一个实现web应用程序的高级且非常有趣的平台。尽管这是一个相对年轻的项目,还处于起步阶段,但自2009年启动以来已经取得了重大进展。随后几年,该平台得到了发展,并被部署在世界各地许多值得称赞的项目中。

这个平台最有用的特性之一,尤其是对于初学者来说,在于它的简单性和可访问性。要实现项目,开发人员不必手动收集关于Node.js的库、包和信息。更好的是,安装需要几分钟,之后工具就可以工作了。

从结构上讲,该工具是引擎(V8)、I / O和库集的组成的。在这种情况下,应用程序或站点组件的开发是用同一种编程语言—JavaScript进行的。这意味着项目的开发不需要研究额外的资源,也不需要雇佣多个专家用不同的语言编写。在JS上有良好的编程技能,或者雇佣一名拥有这种语言的程序员就足够了。因此,服务器和客户端是用一种语言编写的。


Node.js可以在哪里使用


这个平台不仅非常适合编写服务器应用程序和聊天,还可以使用NW编写桌面应用程序。js或AppJS。尽管如此,它为开发人员提供了集成各种api的灵活性,这大大加快了程序的总体开发时间。

由于其异步性,该平台适合开发聊天室和在线游戏,允许多个用户同时访问系统。也就是说,它不适合实现计算任务,因为这样的任务无法处理交互式系统。

关于Node.js的另一个独特之处是与Apache等同行相比的效率因素。这是因为当连接关闭时它仍然处于休眠状态。我们有待纠正,但那些与Apache合作过的人会证明它需要相当多的空闲服务器才能保持活跃状态,以便为新连接做好准备。另一方面,Node的回调仅在检测到输入信号时激活。

开源项目在起步阶段就失败的原因之一是放弃赞助的固有风险。然而,由于Linux基金会和其他公司赞助,Node.js做得非常好。它得到了微软、Paypal、Joyent、沃尔玛等大公司的赔偿。作为额外的奖励,它是Linux基金会的一部分,因此它将享受长久的生命。

它还用于开发服务器端web应用程序。值得注意的是,最好不要使用关系数据库,因为还没有足够的工具模块。但在适当的时候,它有可能很好地适应这个利基市场。


Node.js开发人员薪水


IT行业的工资使得许多招聘经理的热情高涨。这是因为IT技能是最受追捧的,但同样罕见。

考虑到Node.js的普及以及其应用的无数可能性,如果不了解Node.js的开发人员工资是多么缺乏实用性。在不同的国家。我们对Node.js开发人员的平均工资进行了调查,这就是我们想出的。


为什么Node.js受欢迎?


在过去的几年里,Node.js已经成为各种网站开发的一个非常受欢迎的平台,这也导致了Node.js开发人员的专业普及和需求。

由于能够使用一种语言编写本地、服务器和数据库任务,因此可以实现大规模的跟踪。因此,使用Node.js函数编程创建了以下项目:

  • PayPal支付系统
  • Yahoo搜索引擎
  • 华尔街日报在线版
  • Spotify是最好的照片和视频集合之一

最后的想法


毫无疑问,Node.js将会继续存在。这是因为多个机构正在努力使系统更好。尽管Node.js开发人员的工资非常高,但各国的工资差别很大。话虽如此,始终值得记住的是,技术的可能性与程序员的能力相结合可以带来富有成效的结果。

查看原文

赞 0 收藏 0 评论 0

shine_0123 收藏了文章 · 2018-12-03

理解javascript中的事件循环(Event Loop)

背景

在研究js的异步的实现方式的时候,发现了JavaScript 中的 macrotask 和 microtask 的概念。在查阅了一番资料之后,对其中的执行机制有所了解,下面整理出来,希望可以帮助更多人。

先了解一下js的任务执行机制

首先,javascript是单线程的,所以只能通过异步解决性能问题(否则,如果前面一个任务阻塞了,那么后续的任务都要等待,这种效果是无法接受的)。js在执行代码时存在着两个比较重要的东西:执行栈和任务队列,这两个东西都是用来存储任务的,区别在于:执行栈里面存着的都是同步任务,也就是要按顺序执行的任务;而任务队列中存着的是一些异步任务,这些异步任务一定要等到执行栈清空后才会执行(这句话很重要)。关于任务队列,它还分成两种,一种叫作macrotask queue(姑且这么命名,因为严格来说规范中只有说task,并没有提到macrotask这个概念。这里为了容易区分,可以理解为macrotask=task!=microtask),另一种叫作microtask queue。如果同时考虑node环境和浏览器环境的话,这两种任务分别对应以下api:
microtasks:

  • process.nextTick
  • promise
  • Object.observe
  • MutationObserver

macrotasks:

  • setTimeout
  • setInterval
  • setImmediate
  • I/O
  • UI渲染
  • script标签中的整体代码

javascript在执行时,先从 macrotasks 队列开始执行,取出第一个 macrotask 放入执行栈执行,在执行过程中,如果遇到 macrotask,则将该 macrotask 放入 macrotask 队列,继续运行执行栈中的后续代码。如果遇到microtask,那么将该microtask放入microtask队列,继续向下运行执行栈中的后续代码。当执行栈中的代码全部执行完成后,从microtasks队列中取出所有的microtask放入执行栈执行。执行完毕后,再从macrotasks 队列取出下一个macrotask放入执行栈。然后不断重复上述流程。这一过程也被称作事件循环(Event Loop)。
javascript就是通过这种机制来实现异步的。主线程会暂时存储I/O等异步操作,直接向下执行,当某个异步事件触发时,再通知主线程执行相应的回调函数,通过这种机制,javascript避免了单线程中异步操作耗时对后续任务的影响。

图解事件循环流程

clipboard.png

根据图中描述,一次事件循环的执行步骤如下:
1、从macrotask queue中取出最早的任务
2、在执行栈中执行第一步取出的任务
如果任务中存在microtask,将其压入到microtask queue中
如果任务中存在macrotask,将其压入到macrotask queue中
直到执行完毕
3、执行栈设置为null
4、从macrotask queue中删除执行过的macrotask
5、取出microtask queue中的全部任务,放入执行栈,
如果任务中存在microtask,将其压入到microtask queue中
如果任务中存在macrotask,将其压入到macrotask queue中
注意:这里产生的microtask(也就是microtask产生的microtask )也会在这一步骤中执行。
直到当前microtask queue为空,此步骤结束。
6、执行第一步的操作

实例验证

我们执行如下一段代码,用上面的思路执行,看一下结果是否和预期的一致。

console.log('start')

const interval = setInterval(() => {  
  console.log('setInterval')
}, 0)

setTimeout(() => {  
  console.log('setTimeout 1')
  Promise.resolve()
      .then(() => {
        console.log('promise 3')
      })
      .then(() => {
        console.log('promise 4')
      })
      .then(() => {
        setTimeout(() => {
          console.log('setTimeout 2')
          Promise.resolve()
              .then(() => {
                console.log('promise 5')
              })
              .then(() => {
                console.log('promise 6')
              })
              .then(() => {
                clearInterval(interval)
              })
        }, 0)
      })
}, 0)

Promise.resolve()
    .then(() => {  
        console.log('promise 1')
    })
    .then(() => {
        console.log('promise 2')
    })

按照上面的思路,我们来理一下,预测一下执行结果,看看实际效果是否是这样的。
执行流程:
第一轮:
1、首先这一整段js代码作为一个macrotask先被执行
2、遇到console.log('start'),输出start
3、遇到setInterval,回调函数作为macrotask压入到macrotask queue中,
此时macrotask queue:[setInterval]
4、遇到setTimeout,回调函数作为macrotask压入到macrotask queue中,
此时macrotask queue:[setInterval,setTimeout1]
5、遇到Promise,并且调用了resolve方法,触发了回调,回调作为microtask压入到microtask queue中
此时microtask queue:[promise 1,promise 2]
6、执行栈为空,将microtask queue中的任务放入执行栈
7、执行microtask queue中Promise的回调任务,分别打印promise 1,promise 2
8、执行栈为空,microtask queue为空,开始下一轮事件循环
目前的console中打印内容:
start
promise 1
promise 2
目前macrotask queue:[setInterval,setTimeout1]

第二轮:
1、从macrotask queue中取出最早的任务,这里对应的是第一轮中第3步的回调函数:console.log('setInterval'),输出setInterval
2、setInterval的回调函数作为macrotask压入到macrotask queue中
此时macrotask queue:[setTimeout1,setInterval]
3、执行栈为空,microtask queue为空,开始下一轮事件循环
目前的console中打印内容:
start
promise 1
promise 2
setInterval
目前macrotask queue:[setTimeout1,setInterval]

第三轮:
1、从macrotask queue中取出最早的任务,目前是setTimeout1的回调,将取出的任务放入执行栈执行
2、遇到console.log('setTimeout 1'),输出setTimeout 1
3、遇到Promise,并且调用了resolve方法,触发回调,回调作为microtask压入到microtask queue中
此时microtask queue:[promise 3,promise 4,() => {setTimeout 2}]
4、执行栈为空,将microtask queue中的任务放入执行栈
5、执行microtask queue中Promise的回调任务:
输出promise 3
输出promise 4
将setTimeout 2压入macrotask queue
6、执行栈为空,microtask queue为空,开始下一轮事件循环
目前的console中打印内容:
start
promise 1
promise 2
setInterval
setTimeout 1
promise 3
promise 4
目前macrotask queue:[setInterval,setTimeout2]

第四轮:
1、从macrotask queue中取出最早的任务,这里对应的是setInterval,输出setInterval
2、setInterval的回调函数作为macrotask压入到macrotask queue中
此时macrotask queue:[setTimeout2,setInterval]
3、执行栈为空,microtask queue为空,开始下一轮事件循环
目前的console中打印内容:
start
promise 1
promise 2
setInterval
setTimeout 1
promise 3
promise 4
setInterval
目前macrotask queue:[setTimeout2,setInterval]

第五轮:
1、从macrotask queue中取出最早的任务,目前是setTimeout2的回调,将取出的任务放入执行栈执行
2、遇到console.log('setTimeout 2')输出setTimeout 2
3、遇到Promise,并且调用了resolve方法,触发回调,回调作为microtask压入到microtask queue中
此时microtask queue:[promise 5,promise 6,() => {clearInterval}]
4、执行栈为空,将microtask queue中的任务放入执行栈
5、执行microtask queue中Promise的回调任务:
输出promise 5
输出promise 6
clearInterval清空setInterval计时器
6、执行栈为空,microtask queue为空,macrotask queue为空,任务结束。
最终的console中打印内容:
start
promise 1
promise 2
setInterval
setTimeout 1
promise 3
promise 4
setInterval
setTimeout 2
promise 5
promise 6

通过图片可以看到,结果跟我们的预期一致,在promise2的后面作为方法的返回值,多打印了一个undefined,这个应该好理解的。

clipboard.png图片描述

这里面有个小问题,就是在不同的环境下(node/浏览器),promise4后面的setInterval表现可能会有差异,这里可能跟setTimeout和setInterval的最小间隔有关,虽然我们写成0ms,但实际上这个最小值是有限制的,现阶段不同组织和不同的js引擎实现机制存在差异,不过这个问题不在本次讨论范围之内了。如果我们将上述代码中setInterval的间隔设置为10,那么整个执行流程将严格符合我们的预期。

有什么用?

  • 后续我们在代码中使用Promise,setTimeout时,思路将更加清晰,用起来更佳得心应手。
  • 在阅读一些源码时,对于一些setTimeout相关的骚操作可以理解的更加深入。
  • 理解javascript中的任务执行流程,加深对异步流程的理解,少犯错误。

总结

  • js事件循环总是从一个macrotask开始执行
  • 一个事件循环过程中,只执行一个macrotask,但是可能执行多个microtask
  • 执行栈中的任务产生的microtask会在当前事件循环内执行
  • 执行栈中的任务产生的macrotask要在下一次事件循环才会执行
查看原文

shine_0123 赞了文章 · 2018-12-03

理解javascript中的事件循环(Event Loop)

背景

在研究js的异步的实现方式的时候,发现了JavaScript 中的 macrotask 和 microtask 的概念。在查阅了一番资料之后,对其中的执行机制有所了解,下面整理出来,希望可以帮助更多人。

先了解一下js的任务执行机制

首先,javascript是单线程的,所以只能通过异步解决性能问题(否则,如果前面一个任务阻塞了,那么后续的任务都要等待,这种效果是无法接受的)。js在执行代码时存在着两个比较重要的东西:执行栈和任务队列,这两个东西都是用来存储任务的,区别在于:执行栈里面存着的都是同步任务,也就是要按顺序执行的任务;而任务队列中存着的是一些异步任务,这些异步任务一定要等到执行栈清空后才会执行(这句话很重要)。关于任务队列,它还分成两种,一种叫作macrotask queue(姑且这么命名,因为严格来说规范中只有说task,并没有提到macrotask这个概念。这里为了容易区分,可以理解为macrotask=task!=microtask),另一种叫作microtask queue。如果同时考虑node环境和浏览器环境的话,这两种任务分别对应以下api:
microtasks:

  • process.nextTick
  • promise
  • Object.observe
  • MutationObserver

macrotasks:

  • setTimeout
  • setInterval
  • setImmediate
  • I/O
  • UI渲染
  • script标签中的整体代码

javascript在执行时,先从 macrotasks 队列开始执行,取出第一个 macrotask 放入执行栈执行,在执行过程中,如果遇到 macrotask,则将该 macrotask 放入 macrotask 队列,继续运行执行栈中的后续代码。如果遇到microtask,那么将该microtask放入microtask队列,继续向下运行执行栈中的后续代码。当执行栈中的代码全部执行完成后,从microtasks队列中取出所有的microtask放入执行栈执行。执行完毕后,再从macrotasks 队列取出下一个macrotask放入执行栈。然后不断重复上述流程。这一过程也被称作事件循环(Event Loop)。
javascript就是通过这种机制来实现异步的。主线程会暂时存储I/O等异步操作,直接向下执行,当某个异步事件触发时,再通知主线程执行相应的回调函数,通过这种机制,javascript避免了单线程中异步操作耗时对后续任务的影响。

图解事件循环流程

clipboard.png

根据图中描述,一次事件循环的执行步骤如下:
1、从macrotask queue中取出最早的任务
2、在执行栈中执行第一步取出的任务
如果任务中存在microtask,将其压入到microtask queue中
如果任务中存在macrotask,将其压入到macrotask queue中
直到执行完毕
3、执行栈设置为null
4、从macrotask queue中删除执行过的macrotask
5、取出microtask queue中的全部任务,放入执行栈,
如果任务中存在microtask,将其压入到microtask queue中
如果任务中存在macrotask,将其压入到macrotask queue中
注意:这里产生的microtask(也就是microtask产生的microtask )也会在这一步骤中执行。
直到当前microtask queue为空,此步骤结束。
6、执行第一步的操作

实例验证

我们执行如下一段代码,用上面的思路执行,看一下结果是否和预期的一致。

console.log('start')

const interval = setInterval(() => {  
  console.log('setInterval')
}, 0)

setTimeout(() => {  
  console.log('setTimeout 1')
  Promise.resolve()
      .then(() => {
        console.log('promise 3')
      })
      .then(() => {
        console.log('promise 4')
      })
      .then(() => {
        setTimeout(() => {
          console.log('setTimeout 2')
          Promise.resolve()
              .then(() => {
                console.log('promise 5')
              })
              .then(() => {
                console.log('promise 6')
              })
              .then(() => {
                clearInterval(interval)
              })
        }, 0)
      })
}, 0)

Promise.resolve()
    .then(() => {  
        console.log('promise 1')
    })
    .then(() => {
        console.log('promise 2')
    })

按照上面的思路,我们来理一下,预测一下执行结果,看看实际效果是否是这样的。
执行流程:
第一轮:
1、首先这一整段js代码作为一个macrotask先被执行
2、遇到console.log('start'),输出start
3、遇到setInterval,回调函数作为macrotask压入到macrotask queue中,
此时macrotask queue:[setInterval]
4、遇到setTimeout,回调函数作为macrotask压入到macrotask queue中,
此时macrotask queue:[setInterval,setTimeout1]
5、遇到Promise,并且调用了resolve方法,触发了回调,回调作为microtask压入到microtask queue中
此时microtask queue:[promise 1,promise 2]
6、执行栈为空,将microtask queue中的任务放入执行栈
7、执行microtask queue中Promise的回调任务,分别打印promise 1,promise 2
8、执行栈为空,microtask queue为空,开始下一轮事件循环
目前的console中打印内容:
start
promise 1
promise 2
目前macrotask queue:[setInterval,setTimeout1]

第二轮:
1、从macrotask queue中取出最早的任务,这里对应的是第一轮中第3步的回调函数:console.log('setInterval'),输出setInterval
2、setInterval的回调函数作为macrotask压入到macrotask queue中
此时macrotask queue:[setTimeout1,setInterval]
3、执行栈为空,microtask queue为空,开始下一轮事件循环
目前的console中打印内容:
start
promise 1
promise 2
setInterval
目前macrotask queue:[setTimeout1,setInterval]

第三轮:
1、从macrotask queue中取出最早的任务,目前是setTimeout1的回调,将取出的任务放入执行栈执行
2、遇到console.log('setTimeout 1'),输出setTimeout 1
3、遇到Promise,并且调用了resolve方法,触发回调,回调作为microtask压入到microtask queue中
此时microtask queue:[promise 3,promise 4,() => {setTimeout 2}]
4、执行栈为空,将microtask queue中的任务放入执行栈
5、执行microtask queue中Promise的回调任务:
输出promise 3
输出promise 4
将setTimeout 2压入macrotask queue
6、执行栈为空,microtask queue为空,开始下一轮事件循环
目前的console中打印内容:
start
promise 1
promise 2
setInterval
setTimeout 1
promise 3
promise 4
目前macrotask queue:[setInterval,setTimeout2]

第四轮:
1、从macrotask queue中取出最早的任务,这里对应的是setInterval,输出setInterval
2、setInterval的回调函数作为macrotask压入到macrotask queue中
此时macrotask queue:[setTimeout2,setInterval]
3、执行栈为空,microtask queue为空,开始下一轮事件循环
目前的console中打印内容:
start
promise 1
promise 2
setInterval
setTimeout 1
promise 3
promise 4
setInterval
目前macrotask queue:[setTimeout2,setInterval]

第五轮:
1、从macrotask queue中取出最早的任务,目前是setTimeout2的回调,将取出的任务放入执行栈执行
2、遇到console.log('setTimeout 2')输出setTimeout 2
3、遇到Promise,并且调用了resolve方法,触发回调,回调作为microtask压入到microtask queue中
此时microtask queue:[promise 5,promise 6,() => {clearInterval}]
4、执行栈为空,将microtask queue中的任务放入执行栈
5、执行microtask queue中Promise的回调任务:
输出promise 5
输出promise 6
clearInterval清空setInterval计时器
6、执行栈为空,microtask queue为空,macrotask queue为空,任务结束。
最终的console中打印内容:
start
promise 1
promise 2
setInterval
setTimeout 1
promise 3
promise 4
setInterval
setTimeout 2
promise 5
promise 6

通过图片可以看到,结果跟我们的预期一致,在promise2的后面作为方法的返回值,多打印了一个undefined,这个应该好理解的。

clipboard.png图片描述

这里面有个小问题,就是在不同的环境下(node/浏览器),promise4后面的setInterval表现可能会有差异,这里可能跟setTimeout和setInterval的最小间隔有关,虽然我们写成0ms,但实际上这个最小值是有限制的,现阶段不同组织和不同的js引擎实现机制存在差异,不过这个问题不在本次讨论范围之内了。如果我们将上述代码中setInterval的间隔设置为10,那么整个执行流程将严格符合我们的预期。

有什么用?

  • 后续我们在代码中使用Promise,setTimeout时,思路将更加清晰,用起来更佳得心应手。
  • 在阅读一些源码时,对于一些setTimeout相关的骚操作可以理解的更加深入。
  • 理解javascript中的任务执行流程,加深对异步流程的理解,少犯错误。

总结

  • js事件循环总是从一个macrotask开始执行
  • 一个事件循环过程中,只执行一个macrotask,但是可能执行多个microtask
  • 执行栈中的任务产生的microtask会在当前事件循环内执行
  • 执行栈中的任务产生的macrotask要在下一次事件循环才会执行
查看原文

赞 18 收藏 17 评论 0

shine_0123 赞了回答 · 2018-11-21

解决express加密密码时报错 Digest already called

https://nodejs.org/api/crypto...
一个crypto实例只能调用digest一次
可以这么写:

const crypto = require('crypto');
let yyy = crypto.createHash('md5').update(xxx).digest('hex');

每次调用digest都创建个新crypto实例

关注 4 回答 3

shine_0123 关注了标签 · 2018-09-05

golang

Go语言是谷歌2009发布的第二款开源编程语言。Go语言专门针对多处理器系统应用程序的编程进行了优化,使用Go编译的程序可以媲美C或C++代码的速度,而且更加安全、支持并行进程。
Go语言是谷歌推出的一种全新的编程语言,可以在不损失应用程序性能的情况下降低代码的复杂性。谷歌首席软件工程师罗布派克(Rob Pike)说:我们之所以开发Go,是因为过去10多年间软件开发的难度令人沮丧。Go是谷歌2009发布的第二款编程语言。

七牛云存储CEO许式伟出版《Go语言编程
go语言翻译项目 http://code.google.com/p/gola...
《go编程导读》 http://code.google.com/p/ac-m...
golang的官方文档 http://golang.org/doc/docs.html
golang windows上安装 http://code.google.com/p/gomi...

关注 26113

shine_0123 关注了标签 · 2018-09-05

java

Java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由 Sun Microsystems 公司于 1995 年 5 月推出的 Java 程序设计语言和 Java 平台(即 JavaSE, JavaEE, JavaME)的总称。Java 技术具有卓越的通用性、高效性、平台移植性和安全性。

Java编程语言的风格十分接近 C++ 语言。继承了 C++ 语言面向对象技术的核心,Java舍弃了 C++ 语言中容易引起错误的指針,改以引用取代,同时卸载原 C++ 与原来运算符重载,也卸载多重继承特性,改用接口取代,增加垃圾回收器功能。在 Java SE 1.5 版本中引入了泛型编程、类型安全的枚举、不定长参数和自动装/拆箱特性。太阳微系统对 Java 语言的解释是:“Java编程语言是个简单、面向对象、分布式、解释性、健壮、安全与系统无关、可移植、高性能、多线程和动态的语言”。

版本历史

重要版本号版本代号发布日期
JDK 1.01996 年 1 月 23 日
JDK 1.11997 年 2 月 19 日
J2SE 1.2Playground1998 年 12 月 8 日
J2SE 1.3Kestrel2000 年 5 月 8 日
J2SE 1.4Merlin2002 年 2 月 6 日
J2SE 5.0 (1.5.0)Tiger2004 年 9 月 30 日
Java SE 6Mustang2006 年 11 月 11 日
Java SE 7Dolphin2011 年 7 月 28 日
Java SE 8JSR 3372014 年 3 月 18 日
最新发布的稳定版本:
Java Standard Edition 8 Update 11 (1.8.0_11) - (July 15, 2014)
Java Standard Edition 7 Update 65 (1.7.0_65) - (July 15, 2014)

更详细的版本更新查看 J2SE Code NamesJava version history 维基页面

新手帮助

不知道如何开始写你的第一个 Java 程序?查看 Oracle 的 Java 上手文档

在你遇到问题提问之前,可以先在站内搜索一下关键词,看是否已经存在你想提问的内容。

命名规范

Java 程序应遵循以下的 命名规则,以增加可读性,同时降低偶然误差的概率。遵循这些命名规范,可以让别人更容易理解你的代码。

  • 类型名(类,接口,枚举等)应以大写字母开始,同时大写化后续每个单词的首字母。例如:StringThreadLocaland NullPointerException。这就是著名的帕斯卡命名法。
  • 方法名 应该是驼峰式,即以小写字母开头,同时大写化后续每个单词的首字母。例如:indexOfprintStackTraceinterrupt
  • 字段名 同样是驼峰式,和方法名一样。
  • 常量表达式的名称static final 不可变对象)应该全大写,同时用下划线分隔每个单词。例如:YELLOWDO_NOTHING_ON_CLOSE。这个规范也适用于一个枚举类的值。然而,static final 引用的非不可变对象应该是驼峰式。

Hello World

public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello, World!");
    }
}

编译并调用:

javac -d . HelloWorld.java
java -cp . HelloWorld

Java 的源代码会被编译成可被 Java 命令执行的中间形式(用于 Java 虚拟机的字节代码指令)。

可用的 IDE

学习资源

常见的问题

下面是一些 SegmentFault 上在 Java 方面经常被人问到的问题:

(待补充)

关注 135752

shine_0123 评论了文章 · 2018-08-27

从 1 到完美,用 js 和 electron 写一个桌面应用

从 1 到完美,用 js 和 electron 写一个桌面应用

目前用 js 和前端技术写桌面软件的方案主要有两种:electronnw.js。这两者在底层实现上有所不同,简单的说,electron 是 node + chromium,nw.js 是 node + webkit。从使用上来说,electron 的使用更为广泛,社区也更活跃,有一大批成熟的商用软件,像 vscode, atom等。所以,现在一般推荐使用 electron 来写桌面软件。

1. 选择合适的组件库和工具库

electron 社区多年的积累,已经有很多成熟的第三方组建库和工具库帮助你轻松的构建桌面应用,提高开发效率,避免重复造轮子。

组件库是用来帮助你书写程序的逻辑或者UI:

工具库是用来帮助你构建程序:

更多可以参考 electron community.

2. 选择合适的模板

社区有很多现成的模板,里面封装好了各种工具,打包成应用,创建安装文件等一系列功能,使用者只需要关注怎么编写应用,而不需要关心工具及各个命令的实现及原理。

更多可以参考 https://github.com/search?o=desc&q=electron+boilerplate&s=stars&type=Repositories.

如果你需要扩展功能,可以更改相关的配置文件。

3. 开发应用与运行命令

electron-react-boilerplate 为例。

web部分是以 react + redux + react-router + redux-thunk + react-redux,构建工具部分是以 webpack + babel + eslint + stylelint + prettier + flow + enzyme

另外,它封装了开发工具主要为以下几个命令:

3.1 dev 开发调试命令

npm run dev

这个命令将会运行一个 electron 应用实例,然后在本地开启一个本地 server 服务,当你编写渲染进程代码,本地 server 服务会自动刷新页面,而不需要关闭应用重新运行命令。

3.2 start 以发布模式启动应用

npm run start

与开发调试命令不同的是,它不会开起本地 server 服务,也不会监听文件变动,而是会像发布的应用一样运行。这个一般在发布之前运行,查看应用的运行情况。

3.3 package 打包应用为安装文件

# create installer for linux&win&mac.
npm run package-all

# create installer only for linux.
npm run package-linux

# create installer only for win.
npm run package-win

# create installer only for mac.
npm run package-mac

4. 应用实例

image-viewer 便是使用 electron-react-boilerplate 模板开发的一个图片浏览器应用。

5. 后续

更多博客,查看 https://github.com/senntyou/blogs

作者:深予之 (@senntyou)

版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证

查看原文

认证与成就

  • 获得 5 次点赞
  • 获得 4 枚徽章 获得 0 枚金徽章, 获得 0 枚银徽章, 获得 4 枚铜徽章

擅长技能
编辑

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 2016-02-17
个人主页被 274 人浏览