控制Scratch异步代码的执行顺序

网友投稿 2019-10-26 13:11

你是否在编写项目时遇到过一些棘手的BUG,比如逻辑明明是通顺的,但是执行结果却不符预期。又如两段代码完完全全一模一样,但是结果却大相径庭。这极有可能是代码的执行顺序没有得到控制所导致的,这是一些初学者常常会遇到的问题。本文将分析这种代码执行顺序失控的原因及其应对方法。


01

依照惯例,先来看几个案例。

《恐龙吃苹果》

https://cdn.china-scratch.com/timg/191028/13110a014-0.jpg

舞台布局

https://cdn.china-scratch.com/timg/191028/13110aA1-1.jpg

恐龙代码

使用键盘方向键控制恐龙移动,点击绿旗使恐龙回到起始位置,准备进行下一次游戏。

https://cdn.china-scratch.com/timg/191028/1311105522-2.jpg

苹果代码

判断苹果是否碰到恐龙,一旦碰到则隐藏,当绿旗被点击时将苹果切换至显示状态,准备进行下一次游戏。

案例很普通,代码也普通得不能再普通了,都进行了初始化操作,甚至可以说代码是比较规范的。那么来看一下实际运行效果。

https://cdn.china-scratch.com/timg/191028/13111012R-3.gif

案例效果

注意到问题所在了吗?在图中,一共点击了三次绿旗,第一次,启动程序,控制恐龙移动,苹果被恐龙吃掉-隐藏;第二次点击绿旗,重启程序,恐龙确实回到初始位置了,但是,明明设置了显示状态的初始化,苹果却并没有出现。第三次点击绿旗才出现。

问题是,为什么第二次点击绿旗时,苹果没有按照预想的那样,切换成显示状态呢?

在Scratch中遇到BUG时,有很多种调试方法,这次介绍一种变量法,通过变量来判断某处代码是否执行、执行次数。

https://cdn.china-scratch.com/timg/191028/1311111321-4.jpg

新建变量“score”,在苹果代码中增加【将(score)增加(1)】

为了调试BUG,因此在这里不对变量做初始化。

https://cdn.china-scratch.com/timg/191028/131111IO-5.gif

调试效果

变量score初始值为0,在第一次点击绿旗,被恐龙吃掉后,score的值变为1,此时再次点击绿旗重启程序,恐龙回到初始位置,苹果没有显示,而score的值此时变为2,说明画圈处的代码被执行了两次。

https://cdn.china-scratch.com/timg/191028/13111150D-6.jpg

所以,苹果并不是没有显示,而是显示后再一次碰到恐龙,再次隐藏。唯一合理的解释是,在第二次点击绿旗时,苹果的代码被先执行了,而此时恐龙并没有回到初始位置,因此苹果再次碰到恐龙,再次隐藏。这时候再执行恐龙的代码,恐龙回到初始位置,为时已晚,苹果已经被恐龙“误食”了。

在Scratch中,看似同时执行的代码,实则是有先后顺序的。两段代码没有按照预想的先后顺序执行,导致程序初始化时出现BUG。

很多初学者经常会遇到这样的问题,有的程序点击一次是无法正常运行的,第二次点击才会正常。大多数情况其实和上文中展示的这个案例属于同一种情况。如果再次遇到类似这样的情况,就要好好考虑一下是不是由于没有控制好代码的先后执行顺序而导致初始化BUG。


02

这个问题应该如何解决呢?既然是代码执行顺序不受控制,那么最简单的方法就是通过【等待()秒】积木来调整顺序。我们希望的是恐龙先“离开事发地点”,回到初始位置,再执行苹果是否碰到恐龙的代码,只需要在点击绿旗后,等待一段短暂的时间再执行这段代码即可。

https://cdn.china-scratch.com/timg/191028/131112N20-7.jpg

至于等待的时间,设置成0.1还是0.01其实都可以,但是在这里我建议将数值设为0,即【等待(0)秒】。两段当绿旗被点击积木下的代码的先后执行顺序间隔非常短,等待0秒积木足以改变其顺序。因为【等待()秒】积木会刷新屏幕,而刷新屏幕相对来说耗时较长(参见浅谈Scratch的舞台刷新机制 Part 2——在常规积木中的应用)。

https://cdn.china-scratch.com/timg/191028/1311125O0-8.gif

加入【等待(0)秒】后,苹果初始化正常

这在我看来有两点意义,一是副作用最小,我们的目的是控制程序运行的先后顺序,但是如果等待的时间过久,会造成明显的卡顿,因此在能保证代码运行顺序的前提下,时间越短越好。二是起到一种标志作用,这种特殊的用法我们不妨约定俗成地将其视作为解决这类BUG的标志,一旦看到这样的用法,就知道是用来控制代码执行顺序的。

关于【等待(0)秒】还可以参照科技传播坊疑难杂症视频 vol.41 《等待0秒的意义》。


03

BUG是解决了,但是在《恐龙吃苹果》这个案例中,为什么是先执行苹果的代码再执行恐龙的代码?而不是反过来呢?是什么决定了代码的执行顺序?

为了更直观地表现代码的执行顺序,我们换一个更简单的案例,通过变量来展现执行顺序。

https://cdn.china-scratch.com/timg/191028/1311124R7-9.gif

图层顺序对于异步代码执行顺序的影响

初始状态下,橘猫的代码是将变量设为0,图层在后,蓝猫的代码是将变量设置为1,图层在前。运行结果为0,表明蓝猫的代码先执行了,橘猫后执行,将变量覆盖为0。

通过鼠标拖拽改变图层顺序,将橘猫图层置于蓝猫前面。运行结果为1,表明橘猫的代码先执行了,蓝猫后执行,将变量覆盖为1。

通过实验我们可以得出以下结论:

不同角色的异步代码执行顺序如下:角色图层在前的先执行,图层在后的后执行,此规则亦适用于舞台,其图层永远在最后层。

为排除鼠标拖拽可能产生的其他因素,通过代码调整图层顺序,结论依旧成立。


04

尽管调整图层顺序是一种可控的、确定的能控制异步代码执行顺序的方法,但是编写程序的我们不可能通过手动拖拽角色来控制代码执行顺序。万一体验者在非全屏模式下不小心拖拽了某个角色,改变了执行顺序而导致BUG。代码明明完全一样,但就是无法正常运行,那将会是一件多么尴尬的事。(下次再遇到这样的情况可以考虑一下是否是因为图层顺序影响了代码执行顺序,而又没有很好地控制执行顺序)

为控制代码执行顺序,上文中也提到了,可以使用【等待(0)秒】积木来解决,这种方法简单直接,且对原代码的改动最小,建议新手使用。

但是这是一种“把代码写死”的做法,在此给追求优质代码的scratchers提供一种更完美的思路——消息,利用消息来控制代码的执行顺序。

https://cdn.china-scratch.com/timg/191028/1311133P6-10.jpg

橘猫代码

https://cdn.china-scratch.com/timg/191028/1311131O5-11.jpg

蓝猫代码

https://cdn.china-scratch.com/timg/191028/1311134934-12.jpg

通过【广播()并等待】实现同步

https://cdn.china-scratch.com/timg/191028/1311135596-13.jpg

随手画的流程图

控制代码的执行顺序,你学会了吗?

--end--

声明:本文章由网友投稿作为教育分享用途,如有侵权原作者可通过邮件及时和我们联系删除:freemanzk@qq.com