What is Time Slicing?

Keywords: Programming Fragment

according to W3C performance team More than 50 ms is a long task.

Pictures from Using RAIL model to evaluate performance

According to the figure above, when the delay exceeds 100ms, the user will notice a slight delay.

So to avoid this situation, we can use two schemes, one is Web Worker, the other is Time Slicing.

Web Worker

We all know that JS is a single thread, so when we are running long tasks, it is easy to cause the page to die. Although we can put tasks in the task queue and execute them asynchronously, this does not change the nature of JS.

So in order to change this situation, whatwg Launched Web Workers.

The specific syntax will not be explained, and interested children's shoes can be viewed MDN Web Worker.

We can see the optimization effect after using Web Worker:

const testWorker = new Worker('./worker.js')
setTimeout(_ => {
  testWorker.postMessage({})
  testWorker.onmessage = function (ev) {
    console.log(ev.data)
  }
}, 5000)

// worker.js
self.onmessage = function () {
  const start = performance.now()
  while (performance.now() - start < 1000) {}
  postMessage('done!')
}

Time Slicing

Time slicing is a widely used technical solution. Its essence is to divide long tasks into tasks with short execution time, and then execute them one by one.

This concept is very useful in our daily performance optimization.

For example, when we need to insert a long list into a page at a time (usually, of course, we use paging).

If we use the concept of time slicing to achieve this function, we can use request animation frame + document frame

I will not introduce these two API s in detail. If you are interested, you can check them MDN requestAnimationFrame Follow MDN DocumentFragment.

Here are two DEMO. You can compare the fluency:

Unused time slice:

<style>
    * {
        margin: 0;
        padding: 0;
    }
    .list {
        width: 60vw;
        position: absolute;
        left: 50%;
        transform: translateX(-50%);
    }
</style>
<ul class="list"></ul>
<script>
    'use strict'
    let list = document.querySelector('.list')
    let total = 100000
    for (let i = 0; i < total; ++i) {
        let item = document.createElement('li')
        item.innerText = `I am ${i}`
        list.appendChild(item)
    }
</script>

Use time slice:

<style>
    * {
        margin: 0;
        padding: 0;
    }
    .list {
        width: 60vw;
        position: absolute;
        left: 50%;
        transform: translateX(-50%);
    }
</style>
<ul class="list"></ul>
<script>
    'use strict'
    let list = document.querySelector('.list')
    let total = 100000
    let size = 20
    let index = 0
    const render = (total, index) => {
        if (total <= 0) {
            return
        }
        let curPage = Math.min(total, size)
        window.requestAnimationFrame(() => {
            let fragment = document.createDocumentFragment()
            for (let i = 0; i < curPage; ++i) {
                let item = document.createElement('li')
                item.innerText = `I am ${index + i}`
                fragment.appendChild(item)
            }
            list.appendChild(fragment)
            render(total - curPage, index + curPage)
        })
    }
    render(total, index)
</script>

I didn't do much evaluation, but from the perspective of the user's visual experience, the first scheme is that I want to refresh several times, and there is a white screen phenomenon when I slide down.

In addition to the above scheme of DOM generation, we can also use Web Api requestIdleCallback and ES6 API Generator] to implement it.

I will not introduce too much. Please see the detailed rules MDN requestIdleCallback as well as MDN Generator.

The specific implementation is as follows:

<style>
    * {
        margin: 0;
        padding: 0;
    }
    .list {
        width: 60vw;
        position: absolute;
        left: 50%;
        transform: translateX(-50%);
    }
</style>
<ul class="list"></ul>
<script>
    'use strict'
    function gen(task) {
      requestIdleCallback(deadline => {
        let next = task.next()
        while (!next.done) {
          if (deadline.timeRemaining() <= 0) {
            gen(task)
            return
          }
          next = task.next()
        }
      })
    }
    let list = document.querySelector('.list')
    let total = 100000
    function* loop() {
      for (let i = 0; i < total; ++i) {
        let item = document.createElement('li')
        item.innerText = `I am ${i}`
        list.appendChild(item)
        yield
      }
    }
    gen(loop())
</script>

Reference material

Epilogue

All of the above are my own thoughts. You are welcome to share them. By the way, please pay attention to them. Partners with ideas can comment or send me private letters~

 

Posted by benzrf on Fri, 06 Mar 2020 23:03:48 -0800