星哥の面试题Day14
面试题系列均来自鱼皮的知识星球——编程导航
JS 如何顺序执行 10 个异步任务?
JS 中可以使用 Promise 和 async/await 来顺序执行异步任务。
使用 Promise 可以通过 then() 方法的链式调用来实现顺序执行异步任务,例如:
1 | function asyncTask1() { |
使用 async/await 可以将异步任务看作同步代码来执行,例如:
1 | async function runAsyncTasks() { |
在这里,runAsyncTasks() 函数会先执行异步任务 1,等待异步任务 1 完成后再执行异步任务 2。
React 组件间怎么进行通信?
React 组件间通信可以通过以下方式实现:
- Props 传递:父组件可以通过 Props 将数据传递给子组件,从而实现数据通信。
- Context:Context 是 React 提供的一种组件间通信的机制,可以通过 Context 在组件树中传递数据,避免 Props 层层传递的麻烦。
- Refs:Refs 允许我们直接操作组件实例或者 DOM 元素,从而实现组件间通信。
- 自定义事件:可以通过自定义事件的方式实现组件间的通信。在组件中定义一个事件,当需要在其他组件中触发这个事件时,可以通过回调函数的方式实现。
- 全局状态管理:使用全局状态管理工具(如 Redux、Mobx)来管理组件状态,从而实现组件间通信。
需要根据实际场景选择适合的通信方式。
介绍一下 JS 中 setTimeout 的运行机制?
setTimeout()函数:用来指定某个函数或某段代码在多少毫秒之后执行。它接受两个参数:第一个参数是需要执行的代码块,第二个参数是代码块的延迟时间(以毫秒为单位)。它返回一个整数,表示定时器timer的编号,可以用来取消该定时器。是一个异步函数。
1 | console.log(1); |
最后的打印顺序是:1 3 2 无论setTimeout的执行时间是0还是1000,结果都是先输出3后输出2。
任务队列
一个先进先出的队列,它里面存放着各种事件和任务。 所有任务可以分成两种,一种是同步任务,另一种是异步任务。
同步任务
在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务。
- 输出 如:console.log()
- 变量的声明
- 同步函数:如果在函数返回的时候,调用者就能够拿到预期的返回值或者看到预期的效果,那么这个函数就是同步的。
异步任务
- setTimeout和setInterval
- DOM事件
- Promise
- process.nextTick
- fs.readFile
- http.get
- 异步函数:如果在函数返回的时候,调用者还不能够得到预期结果,而是需要在将来通过一定的手段得到,那么这个函数就是异步的。
优先关系
异步任务要挂起,先执行同步任务,同步任务执行完毕才会响应异步任务。
JS执行机制
由于 JS 是单线程,所以同一时间只能执行一个任务,其他任务就得排队,后续任务必须等到前一个任务结束才能开始执行。 为了避免因为某些长时间任务造成的无意义等待,JS 引入了异步的概念,用另一个线程来管理异步任务。
同步任务直接在主线程队列中顺序执行,而异步任务会进入另一个任务队列,不会阻塞主线程; 等到主线程队列空了(执行完了)的时候,就会去异步队列查询是否有可执行的异步任务了(异步任务通常进入异步队列之后还要等一些条件才能执行,如 ajax 请求、文件读写),如果某个异步任务可以执行了便加入主线程队列,以此循环;
定时器也是一种异步任务,通常浏览器都有一个独立的定时器模块,定时器的延迟时间就由定时器模块来管理,当某个定时器到了可执行状态,就会被加入主线程队列。
setTimeout 注册的函数 fn 会交给浏览器的定时器模块来管理,延迟时间到了就将 fn 加入主进程执行队列,如果队列前面还有没有执行完的代码,则又需要花一点时间等待才能执行到 fn,所以实际的延迟时间会比设置的长; 如在 fn 之前正好有一个超级大循环,那延迟时间就不是一丁点了。
setInterval 的实现机制跟 setTimeout 类似,只不过 setInterval 是重复执行的。 对于 setInterval(fn, 100) 容易产生一个误区: 并不是上一次 fn 执行完了之后再过 100ms 才开始执行下一次 fn。 事实上,setInterval 并不管上一次 fn 的执行结果,而是每隔 100ms 就将 fn 放入主线程队列; 而两次 fn 之间具体间隔多久就不一定了,跟 setTimeout 实际延迟时间类似,和 JS 执行情况有关。