为了账号安全,请及时绑定邮箱和手机立即绑定

如何修改函数内的 var 值?

如何修改函数内的 var 值?

红糖糍粑 2023-05-17 15:53:35
我想设置这个名为 userName 的变量,它应该在 ValueEventListener 中获得一个新值。但是,在函数内部设置新值时,它不会改变。输出仍然是“”private fun getName(){    var userName = ""    val user = fbAuth.currentUser    var uid = user!!.uid    mDatabase = FirebaseDatabase.getInstance().getReference("users")    mDatabase.addValueEventListener(object : ValueEventListener {                override fun onCancelled(p0: DatabaseError) {                    TODO("not implemented")                }                override fun onDataChange(snapshot: DataSnapshot) {                    userName = snapshot.child(uid).child("name").getValue().toString()                }    })    println(userName)}预期输出:John(name child 的值),当前输出:“”
查看完整描述

3 回答

?
侃侃无极

TA贡献2051条经验 获得超10个赞

监听器是异步的,如果你把 println 语句放在username =行下面,它就会打印。

事实上,继续做吧;观察时间戳;哪个先打印?空的还是回调里面的?

var正在被回调修改,但println在 Firebase 发出其值之前很久(在计算机时代,即)首先执行。

此外,我会颠倒行的顺序mDatabase。您本质上是在请求一个值,然后听取结果;结果可能已经发出。您应该先添加侦听器,然后再请求数据。

更新:如果我需要另一个回调的值怎么办?

欢迎来到异步编程的世界:-)

你描述的是一组独立的异步操作。您需要值 A 和值 B,但是在获得值 A 之前您无法获得值 B。两者都是异步的并且需要时间,但是您在主线程上没有时间,或者更确切地说,您有 ~16ms计算、测量和绘制屏幕,以便操作系统可以保持每秒 60 帧的速度。这不是很多时间,也是异步编程存在的部分原因!

简而言之,您想要的是一个对象的实例,一旦操作完成就可以调用它。

在常规同步函数中,每个语句都在另一个语句之后执行,并且在前一个语句未完成之前不会执行任何语句;因此,所有语句都是阻塞语句。

例如:

var times = 2

var message = "Hello"

var world = "World"

println("The message is $message $times")

println(world)

将打印:


The message is Hello 2

World

这是因为执行点会从一行走到另一行,等待上一行执行。如果一个操作需要时间,线程将被阻塞(无法执行任何其他操作)直到该操作完成并且执行点可以移动到下一条指令。


可以想象,iOS 和 Android(以及 Windows、macOS、Linux 等)中的主线程无法被阻止,否则操作系统将无法响应您的触摸和其他发生的事情(例如,手机,如果用户界面没有响应并且您无法点击“接听”),则无法处理来电。


这就是为什么我们使用其他“线程”来卸载不是超快的东西。这伴随着思维方式的改变以及正确的计划,因为现在事情变得更加复杂了。


让我们看一个简单的例子(一些伪代码,所以请原谅任何明显的错误,这只是为了说明这一点,而不是写一个解决方案)。


fun main() {

    var hello = "Hello"

    var message = thisTakesTime()

    println("The message is $hello $message")

    println(hello)

}


fun thisTakesTime(): String {

    // do something that takes 1 second (1000 ms)

    Thread.sleep(1000)

    return "World"

}

这将打印


The message is Hello World

Hello

如您所见,除了整整一秒钟,主线程没有响应外,没有任何变化。例如,如果您要在 Android 上运行它,它会工作,但您的应用程序在 Thread.sleep 期间不会有一秒钟的响应。一秒快,试试10秒;在决定需要 ANR(应用程序未响应)对话框之前,这超过了 Android 操作系统对主线程无响应的 5 秒限制;这就是臭名昭著的“看起来 XXX 应用程序没有响应,等待或关闭”。

你能做什么?

最初,如果你有太多的回调(其中回调 A 在回调 B 完成之前无法执行,而回调 B 在回调 C 完成之前无法执行),并且你开始像那样嵌套它们,你最终会陷入臭名昭著的回调地狱(在 Javascript) ,但适用于任何语言/平台)。

基本上跟踪所有这些异步回调并确保在响应到来时,您的下一个回调已准备就绪,等等是一件痛苦的事情,并且它会引入指数级的复杂性,例如,如果回调 C 在中间失败,现在您必须让回调 B 知道 C 失败了,因此它也必须失败,这反过来又必须让回调 A(原来的!)知道 B 失败了,因此 A 必须对此做些什么,A 是否需要知道 B 因为 C 而失败了吗?还是 A 只关心 B 和 B,而 B 失败背后的原因无关紧要?

好吧,正如您所看到的,即使谈论这个也会变得复杂和混乱,我什至没有涵盖其他同样复杂的可能场景。

我在这里想说的并不是说你不应该使用回调;而是说你不应该使用回调。而是您必须仔细计划何时何地使用它们。

Kotlin 有一些替代方案可以通过使用协程来减少/消除回调地狱,但这些是一个中等高级的主题,它还需要对组件和部件的设计方式进行根本性的改变。

总而言之,对于您的用例,请记住 OOP 的黄金法则:制作做很少事情的小型具体类,并将它们做好。if ()如果您需要在各处开始添加太多,那么您很可能会在各处混合业务逻辑、随机决策和“whatabout”案例。

假设您有一个处理位置数据并将其上传到服务器的类。

您可能会想:

  1. 在 Activity/Fragment(或 ViewModel)中编写所有代码;很快就变得一团糟。

  2. 使用静态方法(或单例模式)创建 LocationUtils;已经一团糟,但也很难测试和模拟。如果您需要不止一种类型的处理怎么办?或者如果你想将它们存储在数据库中,你是否要添加更多的静态方法?

  3. 创建一个小的 LocationProcessor 类,它接收两个点(纬度/经度)在一个小函数中进行处理,并返回处理后的数据,然后创建另一个名为 LocationUploader 的类,它从处理器接收干净的输入,并将其上传到服务器. 这些类都不应该考虑“如果我没有权限怎么办,如果用户关闭位置怎么办”等等。这些问题超出了一个类的责任范围,该类的目的是处理位置坐标,别无其他。应该有其他类负责。请记住,小班级、小职责== 在单个文件中无需担心。

结论?

好吧,此时有更好的答案可以为您提供所需内容的复制粘贴版本;我相信你今天必须从这堵文字墙中拿出的概念是,为了编写现代的、可测试的、简单的功能代码,你计划事情的方式必须发生变化。

长话短说:当事情不是同步的时候,你需要保持一些东西(一个对象)准备好被回调(因此名称回调),监听(或观察)(因此我们称它们为监听器或观察器),发射某物(通常称为 Observable,因为它可以被“观察”)。

祝你好运!


查看完整回答
反对 回复 2023-05-17
?
慕森卡

TA贡献1806条经验 获得超8个赞

正如 Martin 所说,这是一个异步操作,您应该在异步过程完成后处理文本输出:


mDatabase.addValueEventListener(object : ValueEventListener {

                override fun onCancelled(p0: DatabaseError) {

                    TODO("not implemented")

                }

                override fun onDataChange(snapshot: DataSnapshot) {

                    userName = snapshot.child(uid).child("name").getValue().toString()

                    println(userName) //--> Asynchronous request has ended, show the name

                }

    })


查看完整回答
反对 回复 2023-05-17
?
哈士奇WWW

TA贡献1799条经验 获得超6个赞

是的,侦听器是异步的,只有在 onDataChange 方法中打印变量时它才会起作用。


但是,您可以使用回调策略来等待 Firebase 返回数据。是这样的:


interface MyCallback {

    fun onCallback(value: String )

}


fun readData(myCallback: MyCallback){

    mDatabase.addValueEventListener(object : ValueEventListener {

        override fun onDataChange(snapshot: DataSnapshot) {

            userName = snapshot.child(uid).child("name").getValue().toString()

            myCallback.onCallback(value)

        }

    })

}


fun test(){

    readData(object: MyCallback {    

        override fun onCallback(value : String) {

            println(value)

        }

    })


}


查看完整回答
反对 回复 2023-05-17
  • 3 回答
  • 0 关注
  • 150 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信