302019.5

在 leveldb 的使用过程中,发现一个比较忧伤的问题,level 这个包是依赖 levelup 去起一个 leveldb 的,一旦一个实例生成,就会在数据库目录下生成一个 LOCK 文件,这种情况下,你不可以再用另外一个 levelup 去起一个实例。解决的办法是,创建一个函数,用以在多个程序之间共享由 levelup 起来的实例。而且由于 js 是单线程的,所以,只要是在同一个进程中(某些异步情况下要格外小心),就可以避免被锁问题。

17:41:07 已有3条回复
  1. 自己都不知道怎么来的你博客,大概把你所有的杂目录下随便翻了翻觉得up很有意思
    然后往简介里面翻了一下发现还是天健园的学长~
    向学长学习~
    #784 潘小安 2019-05-30 18:00 回复
  2. 相互交流~
    #785 回复给#784 否子戈 2019-06-01 12:30 回复
  3. ~
    #793 回复给#785 潘小安 2019-06-04 14:00 回复
082019.5

在页面闲时执行任务

在页面空闲的时候,执行一些计算或某些特殊的后台任务,这样可以避免对用户操作带来卡顿的问题。虽然js支持异步编程,但是即使某些任务是异步执行的,但是因为js是单线程程序,所以,如果一个任务需要花费比较长的时间去进行计算,那么即使它是在异步回调的时候执行,也会带来界面卡顿,而如果用户在这个卡顿期间进行交互操作,就会明显感到卡死状态,体验不好。

有没有一种办法避免这种情况发生?当然有,最好的方式是使用webworker,启用另外一个线程去执行这个需要消耗大量时间的运算。因为webworker和用户界面所在的线程相互不影响,所以,不会给用户带来卡顿感。当计算完毕之后,通过postMessage实现数据传递,对于用户而言,几乎无感。

但是在一些特殊情况下,我们需要在用户界面所在的主线程去执行这种程序。比如,我们要在这个任务中获取DOM的一些信息。这个时候,我们要想办法让这个任务不对用户的操作造成影响。

web标准提供了requestIdleCallback这个接口,它的用法有点像requestAnimationFrame,它主要用于在浏览器闲时执行某个任务。比如:

var a = 0
requestIdleCallback(() => {
  a ++
})

上面这段代码,给浏览器下了一个命令,当浏览器空闲的时候,执行a ++。

不过requestIdleCallback会有一些兼容性问题,我们只能通过一些手段来使它在低版本浏览器可以用:

export const requestIdleCallback = window.requestIdleCallback || function(cb, delay = 1000) {
  const start = Date.now()
  const action = () => cb({
    didTimeout: false,
    timeRemaining: function() {
      return Math.max(0, 50 - (Date.now() - start))
    },
  })
  const id = setTimeout(() => {
    timeout.id = setTimeout(action, delay)
    timeout.reset = () => {
      clearTimeout(timeout.id)
      timeout.id = setTimeout(action, delay)
    }
    document.addEventListener('keydown', timeout.reset, true)
    document.addEventListener('mousedown', timeout.reset, true)
    document.addEventListener('touchstart', timeout.reset, true)
    document.addEventListener('touchmove', timeout.reset, true)
    document.addEventListener('mousemove', timeout.reset, true)
    window.addEventListener('scroll', timeout.reset, true)
    window.addEventListener('resize', timeout.reset, true)
  })
  var timeout = { id, reset: null }

  return timeout
}
export const cancelIdelCallback = window.cancelIdelCallback || function(timeout) {
  if (!timeout) {
    return
  }

  clearTimeout(timeout.id)
  document.removeEventListener('keydown', timeout.reset)
  document.removeEventListener('mousedown', timeout.reset)
  document.removeEventListener('touchstart', timeout.reset)
  document.removeEventListener('touchmove', timeout.reset)
  document.removeEventListener('mousemove', timeout.reset)
  window.removeEventListener('scroll', timeout.reset)
  window.removeEventListener('resize', timeout.reset)

  timeout.reset = null
  timeout.id = null
}

cancelIdelCallback用于取消前面下达的命令,参数是requestIdleCallback的返回值。

不过有一个问题,就是requestIdleCallback是只执行一次的,如果我们想要做一个守护程序,在浏览器空闲的时候,就开始运行这个守护程序,应该怎么做呢?

/**
 * 创建一个在空闲时执行的任务
 * @param {*} fn
 */
export function autoidle(fn, interval = 1000, immediate = true) {
  var idle
  const run = () => {
    const action = () => {
      idle = requestIdleCallback(run, interval)
    }
    asyncx(fn)().then(action).catch(action)
  }
  const start = () => {
    cancelIdelCallback(idle)
    idle = requestIdleCallback(run, interval)
  }
  const stop = () => {
    cancelIdelCallback(idle)
  }

  if (immediate) {
    start()
  }

  return { start, stop }
}

这个函数可以创建一个守护程序,它可以在你的浏览器空闲的时候不断运行,但你的浏览器开始忙碌的时候,又不会运行的效果。另外,它返回两个函数,start 和 stop,用以在必要的时候停止和重启任务。

17:09:58 已有1条回复
  1. 技术人员的文章看不懂。
    #779 repostone 2019-05-13 17:33 回复
072019.5

监控用户在当前页面的停留时间

我们希望知道一个用户在当前这个页面停留的时间,这中间要考虑到用户通过切换浏览器tab的情况,把所有用户动作都记录下来。

const tracers = []
// 进入
tracers.push({
  type: 'enter',
  time: Date.now(),
})
// 切屏
const visibilitychangeFn = () => {
  if (document.visibilityState === 'hidden') {
    tracers.push({
      type: 'focusout',
      time: Date.now(),
      url: window.location.href,
      requestId: this.getRequestId(),
    })
  }
  else {
    tracers.push({
      type: 'focusin',
      time: Date.now(),
    })
  }
}
document.addEventListener('visibilitychange', visibilitychangeFn)
// 离开
const unloadFn = () => {
  tracers.push({
    type: 'leave',
    time: Date.now(),
    url: window.location.href,
    requestId: this.getRequestId(),
  })
  record({
    type: 'usertracer',
    tracers,
    stay: tracers[tracers.length - 1].time - tracers[0].time,
  })
}
window.addEventListener('beforeunload', unloadFn)

function record(info) {}

通过一个traces数组,记录了用户从进入这个页面,在这个页面停留、切换tab,到最后离开这个页面的时间。

10:19:56 已有3条回复
  1. 现实是你用的几个api好像有兼容问题~
    #780 JC 2019-05-24 14:57 回复
  2. 我博客大部分文章,都是不考虑兼容性的,实现一个东西,不可能面面俱到,最重要的是为读者提供可供参考的思路
    #781 回复给#780 否子戈 2019-05-28 18:17 回复
  3. 这个怎么说呢,
    大部分开发肯定都是要考虑兼容性的,
    就算是参考,
    也是片面的,
    唉,
    不说了
    #782 回复给#781 JC 2019-05-28 18:18 回复