React Native 交互管理器InteractionManager

框架

浏览数:425

2019-10-2

一个可以提升用户体验和交互效果的模块InteractionMnager(交互管理器)

一、基本内容

使用InteractionManager可以让一些耗时的任务在交互操作或者动画完成之后进行执行,这样使用可以保证我们的JavaScript的动画效果可以平滑流畅的执行。可以大大提升用户体验。比如:导航的转场动画

对大多数React Native应用来说,业务逻辑是运行在JavaScript线程上的。这是React应用所在的线程,也是发生API调用,以及处理触摸事件等操作的线程。更新数据到原生支持的视图是批量进行的,并且在事件循环每进行一次的时候被发送到原生端,这一步通常会在一帧时间结束之前处理完(如果一切顺利的话)。如果JavaScript线程有一帧没有及时响应,就被认为发生了一次丢帧。 例如,你在一个复杂应用的根组件上调用了this.setState,从而导致一次开销很大的子组件树的重绘,可想而知,这可能会花费200ms也就是整整12帧的丢失。此时,任何由JavaScript控制的动画都会卡住。只要卡顿超过100ms,用户就会明显的感觉到。

属性方法

runAfterInteractions(task)  静态方法,在用户交互和动画结束以后执行任务
 
createInteractionHandle() 静态方法,创建一个句柄(处理器),通知管理器,某个动画或者交互开始了  

clearInteractionHandle(handler:Handle)  静态方法,进行清除句柄,通知管理器,某个动画或者交互结束了。 
 
setDeadline(deadline:number) 静态方法, 设置延迟时间,该会调用setTimeout方法挂起并且阻塞所有没有完成的任务,然后在eventLoopRunningTime到设定的延迟时间后,然后执行setImmediate方法进行批量执行任务  

Events:CallExpression  

addListener:CallExpression  

在应用开发中我们可以如下进行执行任务

InteractionManager.runAfterInteractions(() => {
   //执行耗时的同步任务
 
});

该模块和其他相关的调度方法对比:

requestAnimationFrame():执行控制动画效果的代码
setImmediate/setTimeout():设置延迟执行任务的时间,该可能会影响到正在执行的动画
runAfterInteractions():延迟执行任务,该不会影响到正在执行的动画效果
触摸系统中的单点或者多点触控都是交互动作,耗时任务会在这些触摸交互动作执行完成之后或者取消以后回调runAfterInteractions()方法进行执行。

InteractionManager也允许应用在动画开始的时候通过createInteractionHandle()方法注册动画生成一个句柄,在结束的时候清除该句柄。

var handle = InteractionManager.createInteractionHandle();
 
//执行动画 (`runAfterInteractions` tasks are queued)
 
//动画执行结束
 
InteractionManager.clearInteractionHandle(handle);
 
//动画清除之后,开始直接runAfterInteractions中的任务

runAfterInteractions任务也可以接收一个普通的回调函数或者一个带有gen方法并且返回一个Promise的PromiseTask对象。如果参数是PromiseTask对象,那么任务是异步执行的,也会阻塞。该会等着当前任务执行完毕以后才能执行下一个任务。

默认情况下,队列任务会一次性在setImmediate方法中批量执行。如果你通过setDeadline方法设置一个时间值,那么任务会在延迟该设定值时间进行执行。这时候会调用setTimeout方法进行挂起任务并且阻塞其他任务的执行。这样可以给触摸交互等操作留出时间更好的相应用户操作

具体使用

InteractionManager.runAfterInteractions(() => {
    //执行耗时的同步任务
    navigate("Redeem", { title: "积分兑换" });
})

或者

constructor(props) {
    super(props);
    this.state = {
        renderPlaceholderOnly: true
    }
}
render(){
    if (this.state.renderPlaceholderOnly) {
        // Toast.loading("加载中...", 1);
       return self._renderPlaceholderView();
    }
    return(
    .....
    )
}
componentDidMount = () => {
    InteractionManager.runAfterInteractions(() => {
        this.setState({ renderPlaceholderOnly: false });
    });
        
};

二、定时器

定时器是一个应用中非常重要的部分。React Native实现了和浏览器一致的定时器Timer

提供如下方法

setTimeout, clearTimeout
setInterval, clearInterval
setImmediate, clearImmediate
requestAnimationFrame, cancelAnimationFrame

setTimeout (fn, 1000) 和 setInterval (fn,1000)

和web中的意思一样,前者表示延迟1000毫秒后执行 fn 方法 ,后者表示每隔1000毫秒执行 fn 方法。

requestAnimationFrame(fn)和setTimeout(fn, 0)不同,

前者会在每帧刷新之后执行一次,而后者则会尽可能快的执行(在iPhone5S上有可能每秒1000次以上)。

setImmediate则会在当前JavaScript执行块结束的时候执行,就在将要发送批量响应数据到原生之前。注意如果你在setImmediate的回调函数中又执行了setImmediate,它会紧接着立刻执行,而不会在调用之前等待原生代码。

Promise的实现就使用了setImmediate来执行异步调用。

注意点:

1、定时器功能比较简单,注意在es6中使用时,需铭记在unmount组件时清除(clearTimeout/clearInterval)所有用到的定时器。

2、可以使用定时器实现一些普通功能:如短信倒计时等

3、对于一些需要延迟执行的特殊场景也可以使用Timer,譬如:目前RN提供的fetch是没有提供设置超时时间的,如果客户端请求后端的一个接口,接口超时了(后端服务设置的超时时间为10s),那么RN界面就一直loading,也不能aborded。那么这时候我们就可以巧妙的使用计时器,如果客户端发出的Request,时间大于某个值(5秒),那么我们直接认为请求失败。

4、今天还发现一个使用setTimeout的场景,在列表页加载下一页的时候,如果接口响应很快,就不会出现loading的效果,这个时候为了有loading的效果,设置一个500毫秒的延时

作者:路人甲