Javascript 长时间设置计时器,即多分钟

Javascript 长时间设置计时器,即多分钟,javascript,firebase,react-native,Javascript,Firebase,React Native,我想将firebase auth与react native一起用于登录和注册,但我得到一个黄色错误: 在Android上,设置一个长时间的计时器(即多分钟)是一个性能和正确性问题,因为它会使计时器模块处于唤醒状态,并且计时器只能在应用程序位于前台时调用。有关更多信息,请参见()。(Saw设置超时,持续时间为111862ms) 我如何解决这个问题? 我不想忽视这一点,我想理解这一错误,并以最佳和标准的方式解决它。 这是我的代码: export default class Login exten

我想将firebase auth与react native一起用于
登录
注册
,但我得到一个黄色错误:

在Android上,设置一个长时间的计时器(即多分钟)是一个性能和正确性问题,因为它会使计时器模块处于唤醒状态,并且计时器只能在应用程序位于前台时调用。有关更多信息,请参见()。(Saw设置超时,持续时间为111862ms)

我如何解决这个问题?
我不想忽视这一点,我想理解这一错误,并以最佳和标准的方式解决它。
这是我的代码:

  export default class Login extends Component {
        constructor(props) {
            super(props)
            this.state = {
                email: '',
                password: '',
                response: ''
            }
            this.signUp = this.signUp.bind(this)
            this.login = this.login.bind(this)
        }
        async signUp() {
            try {
                await firebase.auth().createUserWithEmailAndPassword(this.state.email, this.state.password)
                this.setState({
                    response: 'Account Created!'
                })
                setTimeout(() => {
                    this.props.navigator.push({
                        id: 'App'
                    })
                }, 1500)
            } catch (error) {
                this.setState({
                    response: error.toString()
                })
            }
        }
        async login() {
            try {
                await firebase.auth().createUserWithEmailAndPassword(this.state.email, this.state.password)
                this.setState({
                    response: 'user login in'
                })
                setTimeout(() => {
                    this.props.navigator.push({
                        id: 'App'
                    })
                })

            } catch (error) {
                this.setState({
                    response: error.toString()
                })
            }

        }
        render() {
            return (
                <View style={styles.container}>
                    <View style={styles.containerInputes}>
                        <TextInput
                            placeholderTextColor="gray"
                            placeholder="Email"
                            style={styles.inputText}
                          //  onChangeText={(email) => this.setState({ email })}
                            onChangeText={(email) => {console.log(email);}}
                        />
                        <TextInput
                            placeholderTextColor="gray"
                            placeholder="Password"
                            style={styles.inputText}
                            password={true}
                            onChangeText={(password) => this.setState({ password })}
                        />
                    </View>
                    <TouchableHighlight
                        onPress={this.login}
                        style={[styles.loginButton, styles.button]}
                    >
                        <Text
                            style={styles.textButton}
                        >Login</Text>
                    </TouchableHighlight>
                    <TouchableHighlight
                        onPress={this.signUp}
                        style={[styles.loginButton, styles.button]}
                    >
                        <Text
                            style={styles.textButton}
                        >Signup</Text>
                    </TouchableHighlight>
                </View>
            )
        }
    }
导出默认类登录扩展组件{
建造师(道具){
超级(道具)
此.state={
电子邮件:“”,
密码:“”,
响应:“”
}
this.signUp=this.signUp.bind(this)
this.login=this.login.bind(this)
}
异步注册(){
试一试{
等待firebase.auth().createUserWithEmailAndPassword(this.state.email,this.state.password)
这是我的国家({
响应:“已创建帐户!”
})
设置超时(()=>{
这个是.props.navigator.push({
id:'应用程序'
})
}, 1500)
}捕获(错误){
这是我的国家({
响应:error.toString()
})
}
}
异步登录(){
试一试{
等待firebase.auth().createUserWithEmailAndPassword(this.state.email,this.state.password)
这是我的国家({
响应:“用户登录”
})
设置超时(()=>{
这个是.props.navigator.push({
id:'应用程序'
})
})
}捕获(错误){
这是我的国家({
响应:error.toString()
})
}
}
render(){
返回(
this.setState({email})}
onChangeText={(电子邮件)=>{console.log(电子邮件);}
/>
this.setState({password})}
/>
登录
报名
)
}
}

我向Google Firebase团队报告了同样的问题,我认为这是Firebase web SDK的问题,所以我决定放弃Firebase web SDK,因为它在js线程中运行,而不是在本机线程中运行


我发现了。它比firebase web SDK具有更高的性能,并且这个问题消失了

这修复了黄色框和控制台日志。它甚至为世博会修复了它

只需将以下脚本放在代码库的开头

import { YellowBox } from 'react-native';
import _ from 'lodash';

YellowBox.ignoreWarnings(['Setting a timer']);
const _console = _.clone(console);
console.warn = message => {
  if (message.indexOf('Setting a timer') <= -1) {
    _console.warn(message);
  }
};
从'react native'导入{YellowBox};
从“lodash”进口;
ignoreWarnings([“设置计时器]);
const_console=u.clone(console);
console.warn=消息=>{

如果(message.indexOf('Setting a timer')另一种方法是使用react native ignore warnings模块

这甚至适用于世博会应用程序

你可以用下面的方法

import ignoreWarnings from 'react-native-ignore-warnings';

ignoreWarnings('Setting a timer');
也可以在android上运行。

login()
中,您的
设置超时()
调用缺少间隔值。一般来说,如果窗口/选项卡位于后台,浏览器现在不会触发超时/间隔,或者至少不会及时触发。这是为了防止滥用脚本行为,并减少可能正在轮询的脚本的功耗

原则上,您的代码应该可以工作,如果用户在计时器运行时离开窗口,那么当他们返回时,它将完成。从用户体验的角度来看,这可能是您想要的,因为用户看到的是转换,而不是在他们不看的情况下在后台发生的转换。这有助于他们保持心理环境。


黄色框是因为您根据消息设置了过长的计时器(近两分钟)这不太可能是你想要的。JS环境警告你,你正在做的事情不太可能是你想要的。如果你想要的话,你可以将警告静音。

谷歌软件工程师Josh Crowther说:

但是,使用多步骤短持续时间设置超时实际上并不能解决问题。计时器模块仍然保持活动状态,应用程序仍然存在警告中指出的性能问题。这里的问题是,我们有需要长时间设置的用例,react native没有针对该用例进行优化

因此,所有这些都是:这个错误在这里无法修复,我们只能解决这个错误。有一些解决方法(请参阅)可以禁用警告。在我们的代码中禁用警告对问题没有帮助(除了禁用警告),并且添加了完全不必要的额外SDK代码/权重


如果您对提供的解决方案感到不舒服,我建议您就上述问题(即facebook/react native#12981)进行咨询。我认为解决此问题的最贴心的解决方案(直到RN内部解决此问题)是上述解决方案

//将此代码添加到项目中以重置所有超时
const highestTimeoutId=setTimeout(()=>“;”);
for(设i=0;i
在使用Firebase调用时,我仍然会收到一个黄色框警告(在settimeout上),但此后再也不会出现任何并发警告。我不确定在什么时候调用它可能会发生
// add this code to your project to reset all timeouts
const highestTimeoutId = setTimeout(() => ';');
for (let i = 0; i < highestTimeoutId; i++) {
    clearTimeout(i); 
}
import { YellowBox } from 'react-native';

construct() {
    YellowBox.ignoreWarnings(['Setting a timer']);
}
import {Platform, InteractionManager} from 'react-native';

const _setTimeout = global.setTimeout;
const _clearTimeout = global.clearTimeout;
const MAX_TIMER_DURATION_MS = 60 * 1000;
if (Platform.OS === 'android') {
    // Work around issue `Setting a timer for long time`
    // see: https://github.com/firebase/firebase-js-sdk/issues/97
    const timerFix = {};
    const runTask = (id, fn, ttl, args) => {
        const waitingTime = ttl - Date.now();
        if (waitingTime <= 1) {
            InteractionManager.runAfterInteractions(() => {
                if (!timerFix[id]) {
                    return;
                }
                delete timerFix[id];
                fn(...args);
            });
            return;
        }

        const afterTime = Math.min(waitingTime, MAX_TIMER_DURATION_MS);
        timerFix[id] = _setTimeout(() => runTask(id, fn, ttl, args), afterTime);
    };

    global.setTimeout = (fn, time, ...args) => {
        if (MAX_TIMER_DURATION_MS < time) {
            const ttl = Date.now() + time;
            const id = '_lt_' + Object.keys(timerFix).length;
            runTask(id, fn, ttl, args);
            return id;
        }
        return _setTimeout(fn, time, ...args);
    };

    global.clearTimeout = id => {
        if (typeof id === 'string' && id.startsWith('_lt_')) {
            _clearTimeout(timerFix[id]);
            delete timerFix[id];
            return;
        }
        _clearTimeout(id);
    };
}
componentDidMount() { console.disableYellowBox = true; ... }
// Firebase App (the core Firebase SDK) is always required and
// must be listed before other Firebase SDKs
import * as firebase from "firebase/app";
import "firebase/auth";
import {LoginStatus} from "./LoginStatus";
import {auth, User} from "firebase";
import {ApiResponse, DelegateBearerAuthClient, HttpClient} from "@hardcodet/httpclient";
import {Env} from "../../Env";



export class FirebaseClient {

    /**
     * Creates a simple API client to use with Firestore.
     * We don't want to use the JS package's implementation since it has issues with
     * long-running timers - see https://github.com/firebase/firebase-js-sdk/issues/97
     * @param idToken The user's ID token as retrieved through firebase auth
     */
    private static getFirestoreClient(idToken: string) {
        const projectId = Env.firebaseProjectId;
        const baseUri = `https://firestore.googleapis.com/v1/projects/${projectId}/databases/(default)/documents/`

        const authClient = new DelegateBearerAuthClient(async () => idToken);
        return new HttpClient(baseUri, {authClient});
    }


    /**
     * Use firebase auth for login etc. because lazy.
     */
    public static async userLogin(email: string, password: string): Promise<User> {
        try {
            const credentials: firebase.auth.UserCredential = await auth().signInWithEmailAndPassword(email, password);
            return credentials.user;
        } catch (error) {
            console.error(error);
            return undefined;
        }
    }


    private static resolveStatus(errorCode: string): { user: User, status: LoginStatus } {
        switch (errorCode) {
            case "auth/invalid-email":
                return {user: undefined, status: LoginStatus.InvalidEmailAddress};
            case "auth/user-not-found":
                return {user: undefined, status: LoginStatus.UnknownUserId};
            case "auth/wrong-password":
                return {user: undefined, status: LoginStatus.WrongPassword};
            case "auth/email-already-in-use":
                return {user: undefined, status: LoginStatus.EmailAddressAlreadyInUse};
            case "auth/weak-password":
                return {user: undefined, status: LoginStatus.WeakPassword};
            case "auth/user-disabled":
                return {user: undefined, status: LoginStatus.UserDisabled};
            case "auth/expired-action-code":
                return {user: undefined, status: LoginStatus.InvalidActionCode};
            default:
                return {user: undefined, status: LoginStatus.Undefined};
        }
    }


    /**
     * Resolve the user's keys from the backend.
     */
    public static async getSomeUserData(firebaseUserId: string, idToken: string): Promise<...> {

        const client: HttpClient = FirebaseClient.getFirestoreClient(idToken);

        // userData here is the name of my collection in firestore. i'm using the user ID as the document ID
        var result = await client.getAs<any>(`userData/${firebaseUserId}?key=${Env.firebaseApiKey}`);
        if (result.success) {
            const json = result.value;
            const foo = json.fields.foo.stringValue;
            const bar = json.fields.bar.stringValue;
            return ...
        } else {
            if (result.notFound) {
                // that document with that key doesn't exist
                console.warn("no document with key " + firebaseUserId);
                return undefined;
            }
            throw result.createError();
        }
    }


    public static async writeSomeData(idToken: string, firebaseUserId: string, foo: string, bar: string): Promise<...> {

        const data = {
            "fields": {
                "foo": {
                    "stringValue": foo
                },
                "bar": {
                    "stringValue": bar
                }
            }
        };

        // again, just do an HTTP post, use the firebase user ID as the document key
        const client: HttpClient = FirebaseClient.getFirestoreClient(idToken);
        const result: ApiResponse = await client.post(`userData?documentId=${firebaseUserId}&key=${Env.firebaseApiKey}`, data);

        if (result.success) {
            return ...
        } else {
            throw result.createError();
        }
    }


    /**
     * Gets the currently logged in user, if any.
     */
    public static getCurrentUser(): User {
        return auth().currentUser;
    }



    public static async sendPasswordReset(email: string): Promise<LoginStatus> {

        try {
            await auth().sendPasswordResetEmail(email);
            return LoginStatus.Success;
        } catch (error) {
            return FirebaseClient.resolveStatus(error.code).status;
        }
    }


    /**
     * Clears the current user from the session.
     */
    public static async signOut() {
        await auth().signOut();
    }
}
        const user: User = await FirebaseClient.userLogin("me@domain.com", "mypassword");

        const idToken= await user.getIdToken(false);
        const data = await FirebaseClient.getSomeUserData(user.uid, idToken);
import { YellowBox } from 'react-native';
YellowBox.ignoreWarnings(['Setting a timer']);
import { LogBox } from 'react-native';

LogBox.ignoreLogs(['Setting a timer']);
import { LogBox } from 'react-native';
LogBox.ignoreLogs(['Setting a timer for a long period of time'])
import { YellowBox } from 'react-native';
YellowBox.ignoreWarnings(['Setting a timer for a long period of time']);