javascripts中的promise

一、基本概念 1. 异步 所谓 " 异步 “,简单说就是一个任务分成两段, 先执行第一段, 然后转而执行其他任务去, 等做好了准备, 再回过头执行第二段。 比如, 有一个任务是读取文件进行处理, 任务的第一段是向操作系统发出请求, 要求读取文件。 然后, 程序执行其他任务, 等到操作系统返回文件,再接着执行任务的第二段( 处理文件)。 这种不连续的执行, 就叫做异步。 相应地, 连续的执行就叫做同步。 由于是连续执行, 不能插入其他任务, 所以操作系统从硬盘读取文件的这段时间, 程序只能干等着什么也做不了。 2. 回调函数 javascript 语言对异步编程的实现, 就是回调函数。 所谓回调函数, 就是把任务的第二段单独写在一个函数里面, 等到准备好了,继续执行的时候, 就直接调用这个函数。 它的英语名字 callback, 直译过来就是 " 回调 “。 读取文件进行处理, 是这样的,readfile 是异步的,readfileSync 是同步的。 fs.readfile('/etc/passwd', function(err, data) { if(err) throw err; console.log(data); }); 上面代码中, readfile 函数的第二个参数, 就是回调函数, 也就是任务的第二段。 等到操作系统返回了 / etc / passwd这个文件以后, 回调函数才会执行。 ==一个有趣的问题是, 为什么 node.js 约定, 回调函数的第一个参数, 必须是错误对象 err( 如果没有错误, 该参数就是 null)? 原因是执行分成两段, 在这两段之间抛出的错误, 程序无法捕捉, 只能当作参数, 传入第二段。== ...

2022年5月24日

javascripts中的map

Javascripts 中的 map 说到map,如果我们有循环的话,不停遍历,会把时间搞成 O(n)。如果有map,时间就变成了O(1),所以还是相当有效率的。 map 是 ES2015 中新增加的,是一种新的 Object 类型,允许存放各种 key-value 对。 map 最大的特性是,key 可以是任何类型,包括 object 和 function;可以调用 size 得到 map 的大小;迭代的时候,是严格按照添加的顺序进行迭代的。 map 的方法如下: set, get, size, has, delete, clear: let things = new Map(); const myFunc = () => '🍕'; things.set('🚗', 'Car'); things.set('🏠', 'House'); things.set('✈️', 'Airplane'); things.set(myFunc, '😄 Key is a function!'); things.size; // 4 things.has('🚗'); // true things.has(myFunc) // true things.has(() => '🍕'); // false, not the same reference things.get(myFunc); // '😄 Key is a function!' things.delete('✈️'); things.has('✈️'); // false things.clear(); things.size; // 0 // setting key-value pairs is chainable things.set('🔧', 'Wrench') .set('🎸', 'Guitar') .set('🕹', 'Joystick'); const myMap = new Map(); // Even another map can be a key things.set(myMap, 'Oh gosh!'); things.size; // 4 things.get(myMap); // 'Oh gosh!' 迭代它的方法,可以用 for … of,顺序是严格按照你插入的顺序来的: ...

2022年5月23日

javascript的实际应用-fs模块

千里之行始于足下,javascript 模块漫天飞,能做的事也是五花八门。我们来实践一下 假设我们有一个文本文件,内容如下: 里面是一行行数据,我们要做的就是把所有值取整求和,文件中有某些空行 很简单,程序如下: var fs = require('fs'); calculate = () => { fs.readFile('data.txt', 'utf8', (err, data) => { if (err) { throw new Error(err) } const arr = data.split('\r\n'); const result = arr .filter(e => e) .map(parseFloat) .reduce((curr, next) => curr + next); console.log('RESULT: ', result); }); } 超级简单吧 关键就是上面的链式调用 split 用来分割每一行 filter 用来去掉空行 map 用来把每一行都转化成整数 reduce 用来求和

2022年5月22日

javascripts中的module

JavaScript Module 是什么 一个 JavaScript module 模块准确的说就是一个文件,它允许你把代码 export 导出来复用,这样别的 JavaScript 文件可以 import 这个文件把它作为库文件来使用了。 模块主要是用在工程文件中,用来把代码共享给其他文件用,javascipt 的模块可以说是满天飞。 这里呢讨论的是前端的部分,即是运行在浏览器中的 javascripts module 部分,而不是运行在后端的 Node.js 的部分,后端现在已经向前端靠拢了。 ES6标准发布后,module成为标准,标准使用是以export指令导出接口,以import引入模块,但是在我们一贯的node模块中,我们依然采用的是CommonJS规范,使用require引入模块,使用module.exports导出接口。这点也理解清楚。 我们从零来一步步开始吧 如何把一个 JavaScript 文件转换为 ES 的 module 1: 创建一个 project 目录 这个目录用来放 HTML 和 JavaScript 文件 2: 建立你的 code 代码文件 在 project 文件夹中建立2个文件: index.html index.js 3: 添加 JavaScript 文件的引用到 HTML 文件中 打开index.html 编辑它: <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>ES Module - zhang ranrui</title> </head> <body> <h1>ES Module Tutorial</h1> <!-- Add the "index.js" JavaScript file to this HTML document --> <script type="module" src="index.js"></script> </body> </html> 上面的代码中,我们引用了 index.js , 并定义它 type=“module”,这显式指明了 index.js 是一个 ES 的module。 ...

2022年5月21日

Javascripts之数组的reduce

研究一下 javascripts 的 reduce ,reduce 是既能改变 array 的 size,又能改变数值的函数,filter 是只能改变size,不能改变数值;而 map 是不能改变 size,可以改变数值。很拗口吧,三个兄弟。 简单介绍一下 reduce。 假设我们有一个数组: [1, 2, 3, 4] 我们要对整个数组求和. reduce 实际是按照下列的算式来进行求和的: ((((1) + 2) + 3) + 4) 那实际 reduce 函数执行中,你可以按你需求来自定义你自己的 + 操作符。数组的值也可以是其他的任意东西。听起来有点意思吧? 1、 Reduce 是干嘛的 在一个函数式编程语言中,reduce 其实有很多别的名称,比如 fold(对折), accumulate(累加器), aggregate(聚合器), compress(压缩) 甚至叫 inject(注入)。 2、 Reduce 的参数 常用用法如下: let myArray = [/* 首先定义一个数组 */]; let callbackfn = /* 再定义一个函数 */ ; let initialvalue = /* 任意一个初始化的值 */ ; myArray.reduce(callbackfn) myArray.reduce(callbackfn, initialValue) reduce 的参数如下: callbackfn: 必须是一个函数,会在整个数组中反复调用,reduce 调用 callbackfn 的时候有4个参数,我们定义它们是 previousValue, currentElement, index 和 array ,看起来像下面一样: ...

2022年5月20日

面试之Docker如何打出最小的镜像

面试的时候被问到:如何才能让 docker 打出的镜像包尽量小? 其实在生产已经尽量使用最小化的镜像包了,只是突然被问到还是有点懵圈;因为印象中自己基本是使用 alphine 做底包,Dockerfile 通常就是 copy 一个可执行的程序进去就完事了,如果不行,再开 shell 进去慢慢添加缺少的库文件。 这里就总结一下,两点: 一、使用尽量小的底包 以 alphine 为主,使用 alphine 底包的时候,需要注意以下: 1、替换 apk 的源 2、更新、更新证书 3、注意 Timezone 的设置 4、注意 glibc 库的兼容问题 二、使用分阶段build 通常类似c、go、rust之类的源代码,都需要经过编译,最后产生可执行文件,那么完整的编译环境其实对最后的镜像来说都是不需要的。 所以利用分阶段build,甩脱编译环境以及中间产物,就可以缩小最后 build 出镜像的大小,使用也很简单。 Dockerfile 文件内容如下: FROM golang:alpine AS build-env WORKDIR /app ADD . /app RUN cd /app && go build -o goapp FROM alpine RUN apk update && \ apk add ca-certificates && \ update-ca-certificates && \ rm -rf /var/cache/apk/* WORKDIR /app COPY --from=build-env /app/goapp /app EXPOSE 8080 ENTRYPOINT ./goapp

2022年4月1日

面试之Nginx的epoll的优势

面试时被问到:是否了解 Nginx,它使用的 epoll 模式和其他的相比有什么优势? 直接被问住,实际生产中配过不少的 Nginx,各种 rewrite、regex、正反向代理、php、fast-cgi、限流、证书、jwt、cors;epoll 只大概有印象是下面这行: events { use epoll; worker_connections 1024; } 实在是汗颜啊,所以得仔细研究一下这个 epoll。 首先扔概念 IO多路复用: 多路是指网络连接,复用指的是同一个线程。 IO多路复用是一种同步IO模型,实现一个线程可以监视多个文件描述符; 一旦某个描述符就绪(一般是读就绪或者写就绪),就能够通知应用程序进行相应的读写操作; 没有文件描述符就绪时会阻塞应用程序,交出cpu。 那么IO多路复用的实现方法有三种: select、poll、epoll select、poll、epoll本质上都是同步I/O,用户进程(这里就是Nginx)负责读写(从内核空间拷贝到用户空间),读写过程中,用户进程是阻塞的。 select: 查询 fd_set 中,是否有就绪的 fd,可以设定一个超时时间,当有 fd (File descripter) 就绪或超时返回; fd_set 是一个位集合,大小是在编译内核时的常量,默认大小为 1024 特点: 连接数限制,fd_set 可表示的 fd 数量太小了; 线性扫描:判断 fd 是否就绪,需要遍历一边 fd_set; 数据复制:从内核空间拷贝到用户空间,复制连接就绪状态信息 poll: 解决了连接数限制: poll 中将 select 中的 fd_set 替换成了一个 pollfd 数组 解决 fd 数量过小的问题 数据复制:从内核空间拷贝到用户空间,复制连接就绪状态信息 epoll:event 事件驱动 事件机制:避免线性扫描 为每个 fd,注册一个监听事件 fd 变更为就绪时,将 fd 添加到就绪链表 ...

2022年4月1日

Kubernetes下Flannel网络

现在各大公有云的 k8s 网络插件基本用的都是 vxlan,我们也需要对这个进行一下详细了解,以便用于公司的正式生产环境。 一、原理 首先,kubernetes的网络模型: 包含三要素: 所有的容器(container)之间能够在不使用 NAT 的情况下互相通讯 所有的节点(Node)能够在不使用 NAT 的情况下跟所有的容器(container)互相通讯(反之容器访问节点亦然) 容器(container)中的IP地址,在容器内和容器外面看起来是一样的 那来到 flannel,它是一种 Overlay network 覆盖网络,盖在原有的 Node 网络基础上: 上图要仔细分析, K8S 中存在三个+一个网络段: node 网络段,上图是 172.20.32.0/19,这是基础网络段 pod 网络段,100.96.0.0/16,2¹⁶(65536)个IP,这是由 flannel 产生的 overlay network,所有的 pod 都站在一个大广场上,互相可见 svc 网络段,上图未提,我们需要知道,想要把 pod 的 ip 给固定下来,就得使用 svc 来分配固定的域名,这个是由 iptables 来维护的 In-Host docker network网络段,这是每个 Node 主机的单独网络段,flannel给每个 Node 主机划分了一个 100.96.x.0/24 段,然后在 etcd 内进行维护,来避免不同的 Node 主机分配IP冲突。 综述:flannel 为每个 Node 分配一个 subnet,容器(container)从此 subnet 中分配 IP,这些 IP 可以在 Node 间路由,容器间无需 NAT 和 port mapping 就可以跨主机通信。 ...

2022年3月17日

ucarp的安装配置

有 V2EX 的坛友问到如何静态编译 keepalived 的问题,实际上 keepalived 确实配置比较麻烦。那还有一个简单易行的 ucarp,生产也可以用这个。 ucarp 跟 keepalived 一样,都是用于高可用的 IP 漂移 但是比 keepalived 配置简单,而且 opnsense 就是用的这个做的高可用,opnsense 是个非常可靠的防火墙软件。 首先有两台虚机,172.18.19.1和172.18.19.2,虚拟ip是172.18.19.3 先配置172.18.19.1 yum install epel-release yum install psmisc yum install ucarp cat<< EOF >> /etc/systemd/system/ucarp.service [Unit] Description=UCARP virtual interface After=network.target [Service] Type=simple ExecStart=/usr/local/bin/ucarp.sh RemainAfterExit=true ExecStop=/usr/bin/killall -SIGTERM ucarp ExecStop=/bin/sleep 10 TimeoutStopSec=30 StandardOutput=journal [Install] WantedBy=multi-user.target EOF 注意,上面我们的systemd是直接调用了ucarp.sh来启动,这样更简单。 三个脚本,注意权限是755 cat<< EOF >> /usr/local/bin/ucarp.sh #!/bin/bash' /usr/sbin/ucarp -i eth0 -B -p nb1Dshiwode -v 001 -a 172.18.19.3 -s 172.18.19.1 --shutdown --preempt -u /usr/local/bin/vip-up.sh -d /usr/local/bin/vip-down.sh EOF cat<< EOF >> /usr/local/bin/vip-up.sh #!/bin/sh /sbin/ip addr add ${2}/24 dev ${1} /sbin/ip neigh flush dev ${1} EOF cat<< EOF >> /usr/local/bin/vip-down.sh #!/bin/sh /sbin/ip addr del ${2}/24 dev ${1} /usr/sbin/arp -d ${2} EOF chmod 755 /usr/local/bin/ucarp.sh /usr/local/bin/vip-up.sh /usr/local/bin/vip-down.sh ok,第一台机器就配置好了 ...

2022年2月25日

gost tunnel的使用

用过了好几个 tunnel 工具软件,各有长处。 ghostunnel 是支持自定义 tls 证书加密的,这回用个更厉害的,支持各种代理链条的,gost 项目地址:https://github.com/ginuerzh/gost 里面的概念有点意思,其实就两条,-L 本地监听,-F 链条转发。 注意的就一点,-F 的时候实际是一个中转服务器。这个中转服务器要转发的规则需要在 -L 参数里指定。 那么举个具体的例子: 中转服务器开了一个明文端口 9999,这个端口跑的是 ghostunnel client,加密数据后传到另外一个ghostunnel server服务器,然后数据明文穿出,访问具体的目的网站的端口。 客户端的服务器呢无外网访问权限,需要访问中转服务器 192.168.1.1 的 9999 端口,然后数据通过 ghostunnel 加密穿出到外网。 中转服务器配置如下: # ghostunnel 监听本地环回地址的9999端口,TLS 加密穿越到外网110.114.5.19:9999,然后根据外网 server 里面的配置,到达最终目的地 ip 和 端口 ghostunnel client --listen localhost:9999 --target 110.114.5.19:9999 --keystore client.pem --cacert ca.pem --unsafe-listen # gost 监听本地网卡192.168.1.1的9999端口 gost-linux-amd64-2.11.1 -L=tls://192.168.1.1:9999 客户端配置如下: gost-linux-amd64-2.11.1 -L=tcp://localhost:9999/localhost:9999 -F=tls://192.168.1.1:9999 这样客户端的程序,只要连接 localhost:9999 的 tcp 端口,就可以接力中转服务器穿出去了。 上面的配置非常怪异吧,仔细解释一下: 中转服务器呢有两个地址,localhost的环回地址,以及本地网卡192.168.1.1 localhost:9999 是用 ghostunnel 加密 tcp 流量,tls 穿到外网 110.114.5.19 的 9999 端口,然后外网再转发。 ...

2022年2月24日