定时器是一个应用中非常重要的部分。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来执行异步调用。
InteractionManager(交互管理器)
原生应用感觉如此流畅的一个重要原因就是在互动和动画的过程中避免繁重的操作。在react Native里,我们目前受到限制,因为我们只有一个javascript执行线程。不过你可以用InteractionManager来确保在执行繁重工作之前所有的交互和动画都已经处理完毕。
应用可以通过以下代码来安排一个任务,使其在交互结束之后执行:
- InteractionManager.runAfterInteractions(() => {
- // ...需要长时间同步执行的任务...
- });
我们来把它和之前的几个任务安排方法对比一下:
requestAnimationFrame(): 用来执行在一段时间内控制视图动画的代码
setImmediate/setTimeout/setInterval(): 在稍后执行代码。注意这有可能会延迟当前正在进行的动画。
runAfterInteractions(): 在稍后执行代码,不会延迟当前进行的动画。
触摸处理系统会把一个或多个进行中的触摸操作认定为'交互',并且会将runAfterInteractions()的回调函数延迟执行,直到所有的触摸操作都结束或取消了。
InteractionManager还允许应用注册动画,在动画开始时创建一个交互“句柄”,然后在结束的时候清除它。
- var handle = InteractionManager.createInteractionHandle();
- // 执行动画... (`runAfterInteractions`中的任务现在开始排队等候)
- // 在动画完成之后
- InteractionManager.clearInteractionHandle(handle);
- // 在所有句柄都清除之后,现在开始依序执行队列中的任务
TimerMixin
我们发现很多React Native应用发生致命错误(闪退)是与计时器有关。具体来说,是在某个组件被卸载(unmount)之后,计时器却仍然被激活。为了解决这个问题,我们引入了TimerMixin。如果你在组件中引入TimerMixin,就可以把你原本的setTimeout(fn, 500)改为this.setTimeout(fn, 500)(只需要在前面加上this.),然后当你的组件卸载时,所有的计时器事件也会被正确的清除。
这个库并没有跟着react native一起发布。你需要在项目文件夹下输入npm i react-timer-mixin --save来单独安装它。
- var TimerMixin = require('react-timer-mixin');
- var Component = React.createClass({
- mixins: [TimerMixin],
- componentDidMount: function() {
- this.setTimeout(
- () => { console.log('这样我就不会导致内存泄露!'); },
- 500
- );
- }
- });
我们强烈建议您使用react-timer-mixin提供的this.setTimeout(...)来代替setTimeout(...)。这可以规避许多难以排查的BUG。
译注:Mixin属于ES5语法,对于ES6代码来说,无法直接使用Mixin。
如果你的项目是用ES6代码编写,同时又使用了计时器,那么你只需铭记在unmount组件时清除(clearTimeout/clearInterval)所有用到的定时器。
那么也可以实现和TimerMixin同样的效果。例如:
- import React,{
- Component
- } from 'react-native';
- export default class Hello extends Component {
- componentDidMount() {
- this.timer = setTimeout(
- () => { console.log('把一个定时器的引用挂在this上'); },
- 500
- );
- }
- componentWillUnmount() {
- // 如果存在this.timer,则使用clearTimeout清空。
- // 如果你使用多个timer,那么用多个变量,或者用个数组来保存引用,然后逐个clear
- this.timer && clearTimeout(this.timer);
- }
- };
下面是一个定时器做的计数器(计时器)截图:
以下是代码:
|
/**
* Sample React Native App
* https://github.com/facebook/react-native
* @flow
*/
import React, { Component } from
'react';
import {
AppRegistry,
StyleSheet,
Text,
View,
TextInput,
TouchableOpacity
} from
'react-native';
export
default
class AnimateDemo
extends Component {
constructor(props){
super(props);
this.state={
data:
0
}
this._index=
0;
this._timer=
null;
}
countTime(){
this._timer=setInterval(()=>{
this.setState({data:
this._index--});
if(
this.state.data<=
0){
this._timer && clearInterval(
this._timer);
alert(
"时间到了");
}},
1000);
}
stopTime(){
this._timer && clearInterval(
this._timer);
}
componentWillUnmount() {
this._timer && clearInterval(
this._timer);
}
render() {
return (
<View style={styles.container}>
<Text>
请选择时长(s)<
/Text>
<TextInput onChangeText={(txt)=>{
this.setState({data:txt});
this._index=txt;}}>
<
/TextInput>
<View style={styles.showTime}>
<Text style={styles.timeText}>
{
this.state.data}
<
/Text>
<
/View>
<View style={styles.btngroup}>
<TouchableOpacity style={styles.btn} onPress={
this.countTime.bind(
this)
}>
<Text>
开始<
/Text>
<
/TouchableOpacity>
<TouchableOpacity style={styles.btn} onPress={
this.stopTime.bind(
this)}>
<Text>
暂停<
/Text>
<
/TouchableOpacity>
<
/View>
<
/View>
);
}
}
const styles=StyleSheet.create({
container:{
flex:
1,
},
btngroup:{
flexDirection:
'row',
justifyContent:
'space-around'
},
showTime:{
height:
100,
alignItems:
'center'
},
btn:{
justifyContent:
'center',
width:
60,
height:
40,
backgroundColor:
'#7ecfe8',
alignItems:
'center'
},
timeText:{
color:
'red',
fontSize:
22,
}
})
AppRegistry.registerComponent(
'AnimateDemo', () => AnimateDemo);
|
注意点:
1、定时器功能比较简单,注意在es6中使用时,需铭记在unmount组件时清除(clearTimeout/clearInterval)所有用到的定时器。
2、可以使用定时器实现一些普通功能:如短信倒计时等
3、对于一些需要延迟执行的特殊场景也可以使用Timer,譬如:目前RN提供的fetch是没有提供设置超时时间的,如果客户端请求后端的一个接口,接口超时了(后端服务设置的超时时间为10s),那么RN界面就一直loading,也不能aborded。那么这时候我们就可以巧妙的使用计时器,如果客户端发出的Request,时间大于某个值(5秒),那么我们直接认为请求失败。
4、今天还发现一个使用setTimeout的场景,在列表页加载下一页的时候,如果接口响应很快,就不会出现loading的效果,这个时候为了有loading的效果,设置一个500毫秒的延时,呵呵....