JS Asynchronous Programming and Frequent Exam Interview Questions

wang xiao bo 's photo
wang xiao bo

Published on Dec 12, 2021

5 min read

Subscribe to my newsletter and never miss my upcoming articles

1.The difference between concurrency and parallelism Asynchrony and the knowledge points in this section are actually not the same concept, but these two terms are indeed knowledge points that many people often confuse. In fact, the reason for the confusion may be that the two nouns are similar in Chinese, but they are completely different words in English.

Concurrency is a macro concept. I have task A and task B respectively. These two tasks are completed by switching between tasks within a period of time. This situation can be called concurrency.

Parallel is a micro concept. Assuming that there are two cores in the CPU, then I can complete tasks A and B at the same time. The situation where multiple tasks are completed at the same time can be called parallel.

2.async and await If a function is added with async, then the function will return a Promise

async function test() {
  return "1"
}
console.log(test()) // -> Promise {<resolved>: "1"}

Async is to wrap the return value of the function with Promise.resolve(), which is the same as processing the return value in then, and await can only be used with async

async function test() {
  let value = await sleep()
}

Async and await can be said to be the ultimate asynchronous solution. Compared with the direct use of Promise, the advantage is that the call chain of the then can be processed, and the code can be written more clearly and accurately. After all, writing a lot of then is also very disgusting, and can also Solve the callback hell problem gracefully. Of course, there are some shortcomings, because await transforms asynchronous code into synchronous code. If multiple asynchronous codes have no dependencies but use await, it will cause performance degradation.

async function test() {
// If the following code has no dependencies, you can use Promise.all
// If there is a dependency, it is actually an example of solving the callback hell
await fetch(url)
await fetch(url1)
await fetch(url2)
}

Let's look at an example of using await:

let a = 0
let b = async () => {
  a = a + await 10
  console.log('2', a) // -> '2' 10
}
b()
a++
console.log('1', a) // -> '1' 1

You may have doubts about the above code, let me explain the reason -First, the function b is executed first. Before the execution to await 10, the variable a is still 0. Because await implements a generator internally, the generator will keep things in the stack, so at this time a = 0 is saved

-Because await is an asynchronous operation, if the subsequent expression does not return a Promise, it will be wrapped into Promise.reslove (return value), and then the synchronous code outside the function will be executed

-After the synchronous code is executed, the asynchronous code will be executed, and the saved value will be used. At this time, a = 0 + 10

The above explanation mentioned that await implements a generator internally. In fact, await is the syntactic sugar of generator plus Promise, and an automatic generator is implemented internally. If you are familiar with co, you can actually implement such syntactic sugar yourself.

3.Common timer functions

Asynchronous programming is of course indispensable for timers. Common timer functions include setTimeout, setInterval, and requestAnimationFrame. Let's talk about the most commonly used setTimeout first. Many people think that setTimeout is how long the delay is, and then how long it should be executed afterwards.

In fact, this view is wrong, because JS is executed in a single thread. If the previous code affects performance, it will cause setTimeout to not be executed on schedule. Of course, we can modify setTimeout through code to make the timer relatively accurate

let period = 60 * 1000 * 60 * 2
let startTime = new Date().getTime()
let count = 0
let end = new Date().getTime() + period
let interval = 1000
let currentInterval = interval
function loop() {
count++
// time spent in code execution
let offset = new Date().getTime()-(startTime + count * interval);
let diff = end-new Date().getTime()
let h = Math.floor(diff / (60 * 1000 * 60))
let hdiff = diff% (60 * 1000 * 60)
let m = Math.floor(hdiff / (60 * 1000))
let mdiff = hdiff% (60 * 1000)
let s = mdiff / (1000)
let sCeil = Math.ceil(s)
let sFloor = Math.floor(s)
// Get the time consumed by the next loop
currentInterval = interval-offset
console.log('Hour:'+h,'Minutes:'+m,'Milliseconds:'+s,'Seconds rounded up:'+sCeil,'Code execution time:'+offset,'Next cycle interval' +currentInterval)
setTimeout(loop, currentInterval)
}
setTimeout(loop, currentInterval)

Next, let's look at setInterval. In fact, the function of this function is basically the same as setTimeout, except that the function is a callback function that is executed every once in a while.

Generally speaking, setInterval is not recommended. First, it, like setTimeout, cannot guarantee that the task will be executed at the expected time. Second, it has the problem of execution accumulation, please see the following pseudo code

function demo() {
  setInterval(function(){
    console.log(2)
  },1000)
  sleep(2000)
}
demo()

The above code is in the browser environment, if a time-consuming operation occurs during the execution of the timer, multiple callback functions will be executed at the same time after the time-consuming operation ends, which may cause performance problems. If you have a need for a cyclic timer, you can actually achieve it through requestAnimationFrame

function setInterval(callback, interval) {
  let timer
  const now = Date.now
  let startTime = now()
  let endTime = startTime
  const loop = () => {
    timer = window.requestAnimationFrame(loop)
    endTime = now()
    if (endTime - startTime >= interval) {
      startTime = endTime = now()
      callback(timer)
    }
  }
  timer = window.requestAnimationFrame(loop)
  return timer
}

let a = 0
setInterval(timer => {
  console.log(1)
  a++
  if (a === 3) cancelAnimationFrame(timer)
}, 1000)

First of all, requestAnimationFrame has its own function throttling function, which can basically guarantee that it will only be executed once in 16.6 milliseconds (without frame dropping), and the delay effect of this function is accurate, and there is no problem of inaccurate timer time. Of course You can also implement setTimeout through this function.

 
Share this