分类 大前端 下的文章

Node子进程执行ping操作,获取统计信息


需求:采用ping -t方式不断进行ping操作,直到收到关闭信号or某个超时时间时结束操作,获取统计信息。

分析:

在cmd窗口进行ping -t操作,会一直进行ping,直到输入ctrl+C会输出ping统计信息。

kill('SIGINT') 即模拟ctrl+C终止进程

编码:

这里我自己手动进行统计信息,原因见下面分析。

var exec = require('child_process').exec;
var iconv = require('iconv-lite');
let ping = exec(`ping www.google.com.hk -t`, { encoding: 'binary'}, function (err, stdout, stderr) {
    let send = 0
    let accept = 0
    let lost = 0
    let min = Infinity
    let max = -Infinity
    let avg = 0
    let Min = (a,b)=>a<b?a:b;
    let Max = (a,b)=>a>b?a:b;

    let str = iconv.decode(new Buffer(stdout, 'binary'), 'GBK')
    console.log(str);
    console.log('=========')
    let regAccept = /来自 .*的回复: 字节=(\d+) 时间=(\d+)ms TTL=(\d+)/g
    let regAll = /\n/g
    send = str.match(regAll).length - 2
    send=send<0?0:send
    let res
    while (res = regAccept.exec(str)) {
        accept++
        let tim = Number(res[2])
        min=Min(tim,min)
        max=Max(tim,max)
        avg= (avg*(accept-1)+tim)/accept
        console.log(res[1], tim, res[3],avg)
    }
    console.log('=========')
    console.log(`发送:${send};接收:${accept};丢失:${send-accept};${(1-(accept/send))*100}%丢失`)
    console.log(`最短:${min}ms;最长:${max}ms;平均:${avg}ms`)
});
ping.on('close', (code) => { console.log('close by', code) })
setTimeout(function () {
    ping.kill('SIGINT')
}, 5 * 1000);

想通过ping.kill('SIGINT')去关闭exec子进程。

测试结果是:输出了close by null后,程序依然再运行,并且没有输出统计信息。


setInterval与settimeout模拟的区别


首先先说明下,node里面的事件循环和浏览器中的是不一致的。

这边浏览器用的是chrome 64

问题1:setInterval(fn,ms)过程,是先把fn放入timer堆,还是先执行?

问题2:setInterval 和 settimeout模拟定时 的使用场景都有哪些?

A1

看了一篇文章,里面讲到node中timers阶段的源码为

void uv__run_timers(uv_loop_t* loop) {
  struct heap_node* heap_node;
  uv_timer_t* handle;

  for (;;) {
    heap_node = heap_min((struct heap*) &loop->timer_heap);//取出timer堆上超时时间最小的元素
    if (heap_node == NULL)
      break;
    //根据上面的元素,计算出handle的地址,head_node结构体和container_of的结合非常巧妙,值得学习
    handle = container_of(heap_node, uv_timer_t, heap_node);
    if (handle->timeout > loop->time)//如果最小的超时时间比循环运行的时间还要小,则表示没有到期的callback需要执行,此时退出timer阶段
      break;

    uv_timer_stop(handle);//将这个handle移除
    uv_timer_again(handle);//如果handle是repeat类型的,重新插入堆里
    handle->timer_cb(handle);//执行handle上的callback
  }
}

里面说到对于repeat类型的handle(setInterval设置的),是先重新插入再执行callback

( 我这边测试发现chrome 64是这样的,node相反。可能node修改过实现?

测试代码

var speed = 1000
var start = Date.now()
var icounter = 0
var tcounter = 0
//t:ms
function sleep(t) {
    let d = Date.now()
    while (Date.now() - d < t) { }
}
setInterval(function () {
    var time = (Date.now() - start) / 1000
    var avg = ++icounter / time
    console.log('<td>setInterval</td><td>' + icounter + '</td><td>' + time.toFixed(3) + '</td><td>' + avg.toFixed(6) + '</td>')
    sleep(50)
}, speed)

浏览器运行效果:

setInterval=>次数:1   所用时间:1.002
setInterval=>次数:2   所用时间:2.001
setInterval=>次数:3   所用时间:3.000
setInterval=>次数:4   所用时间:4.002
setInterval=>次数:5   所用时间:5.002

node运行效果:

setInterval=>次数:1   所用时间:1.003
setInterval=>次数:2   所用时间:2.060
setInterval=>次数:3   所用时间:3.114
setInterval=>次数:4   所用时间:4.164
setInterval=>次数:5   所用时间:5.215

结果分析:

  1. 浏览器先把fn放入timer堆,再执行

  2. node先执行,再把fn放入timer堆


趣味js-只用1和特殊字符生成字符串


180306更新:发现有个http://www.jsfuck.com/ ,上面就做了我想做的东西。也找到了C的生成方法

同时,由于+[]=0,+!+[]=1。我们可以把1也给省略


前言

今天逛justjavac 老哥的博客看到了类似下面的代码

(+([]+(11^(1<<1))+((1+1)<<(1+1))+(11>>>1)+(1^1)+((11>>1)+(1<<1))+(-~1)+(-~1+1)+(1^1)+(1^1)))[(!!1+[])[1^1]+([]+{}+[])[1]+(([]+{})[([]+{})[11-1>>1]+[[],[]+{}+[]][[]+1][1]+(/^/[1]+[])[1|1>>1|1]+[{},11^1,!{}+[]][1+1][1<<1^1]+(11/!{}+{})[~1+(11^1)+~1]+[!!{}+{}][[]&111][1&1]+(/^/[111]+[])[11^11]+[{},[{}]+{},1][1+[]][11-~1+11>>1]+(!!1+{})[1&1>>1]+([]+{1:1}+[])[1|1]+[[]+!!1][111>>>111][1<<1>>1]]+[])[([]+![111])[1|1<<1|1]+[/=/,[]+[][11]][1|[]][1>>1]+([{}]+{})[1+!![1]]+[1,!1+/~/][1%11][1^1<<1]+(!!1+[])[1^1]+[!!/-/+/-/][11%11][+!!1]](11^1<<1,-~11>>1)](~1-~(11^1)<<1<<1)

输出:gahing

实现的原理是什么?

原理

了解 js 隐式类型转换(不懂可以参考这里)的都知道

我们可以通过执行!1+[]得到"false"

具体原理是 false+object 操作会先去调用object的valueOf()方法 发现其值=this

继续调用toString()方法得到"", 结果即false+"" = "false"(又去做了隐式转换)

故我们通过数组下标就可以拿到想要的字符f,a,l,s,e

类似的方法我们还能够拿到

!1+[] = "false"
!!1+[] = "true"
1/[]+[] = "Infinity"
[]/[]+[] = "NaN"
[]+{} = "[object Object]"
[]+/^/[1] = "undefined" /* /^/是正则 */
[]+/\:@$/ = "/\:@$/" /*键盘可见特殊字符放/\ /其中(\用于转义)获取比如拿:就是[1]*/

//可拿到的小写字母有=abcdef ijln orst uy

然后你会发现,26个字母还是有好多不在上面的,并不能通过每次去上面拿字符然后再做拼接


puppeteer初探-测试webrtc


介绍

puppeteer是什么?

它是一个nodejs的库,支持调用Chrome的API来操纵Web 它的dom操作可以完全在内存中进行模拟而不打开浏览器

功能强大,可用于截图、pdf生成、UI测试、表单提交、数据爬取、性能诊断...

官方接口地址:https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md

安装

通过npm i puppeteer安装

正常是会报错的。

解法方法1:

1.采用cnpm i puppeteer

解法方法2:

1.使用npm i --save puppeteer --ignore-scripts安装并忽略chromium的下载 2.自行下载 chromium 或 采用本地chrome (后面会说明这样的好处)


Vue 第一个组件,浏览器后退无法触发beforeRouteLeave的问题与解决


已提issue: https://github.com/vuejs/vue-router/issues/1951

现象

加载第一个组件(这里的第一个意思是浏览器历史记录的第一个,后文称为待监听组件)时,正常跳转其他页面可以触发beforeRouteLeave。 但是按浏览器的后退按钮监听不到该事件。

解决方案

目前采用比较土且不实用的解决方案。加一层组件,再router.push到待监听组件,使得待监听组件非第一个组件,可以正常监听beforeRouteLeave事件。

注意点:

由于打乱了原来的路由。需要在main.js中添加全局的路由监听

router.beforeEach((to, from, next) => {
  if (to is '用于跳转的组件' && from is '待监听组件') {
    router.go(-1) 
    next(false)
  } else {
    next()
  }
  // 这样当从一个普通页面A进入待监听组件时,在待监听组件中按返回键时能正常进入A
})

存在的问题:

  1. 若访问不是访问中转组件而是直接访问待监听组件,则没有效果
  2. 增加路由后,多了白屏时间。

可能官方有什么解决方案,或者我用错了。欢迎评论

后记:

VUE路由控制是基于h5的history API实现

beforeRouteLeave又是基于window.onpopstate

该事件的官方描述:

Note that just calling history.pushState() or history.replaceState() won't trigger a popstate event. The popstate event is only triggered by doing a browser action such as clicking on the back button (or calling history.back() in JavaScript). And the event is only triggered when the user navigates between two history entries for the same document.

简而言之:该事件仅在按了浏览器按钮或者调用history.back()方法时生效,且在同一document的两条历史记录条目间的导航才会生效,而上文出现的原因正是不属于同一document