猫史档案馆


[Box3] async 与 await

用户:芜湖咸鱼芜湖咸鱼查看:12 回复:18 评论:12 创建时间:2021-03-02T21:54:24


众所周知,JS是事件驱动的,很多地方都会用到回调函数(比如world.onTick)

如果你是从Kitten或者Box2.0来的,你可能会想,怎么找不到"等待[ 1 ]秒"的代码呢?

其实是有的,只不过我相信官方不会很推荐这种做法

这个函数就是setTimeout(cb: Function, timeMS: number)

这个函数的作用就是在指定时间(以毫秒为单位)后执行指定的函数,对于简单的等待来说确实很方便,但是如果要等一段时间,干一件事,再等一段时间,干第二件事.... 以此类推,回调就会越套越多,比如这样:

setTimeout(()=>{
  // do something...
  setTimeout(()=>{
    // do someting...
    setTimeout(()=>{
      // do someting...
    },1000)
  },1000)
},1000)

这样就叫做"回调地狱",因为不仅写着很麻烦,而且弄错一个括号就会造成很难修的bug

而这时就有一种优雅的写法,叫做Promise,这个东西可以形成一个阻塞,并且可以控制何时结束阻塞,这就能让程序在某处暂停一些时间

而执行这个阻塞就要使用await关键字,后面加上一个Promise对象(或是返回Promise的函数),并且还要注意在await最近的函数前要加上async,让这个函数变为异步函数,否则直接使用await是会报错的~

具体深入Promise来看,它的基本用法大致是这样new Promise(cb: r=> any);

其中传入的回调函数接受一个参数,这个参数也是一个函数,当这个名为r的函数被调用的时候,阻塞就会结束,并在await处返回r的第一个参数值,所以如果要做一个"等待 [1] 秒",就可以这样实现

function wait(ms){
  return new Promise((r)=>{setTimeout(r,ms)});
}


// 使用示例
world.onPlayerJoin(async ()=>{
  await wait(1000)
  world.say("一秒过去了")
})

对于wait函数的定义也可以简写成这样

const wait=ms=>new Promise(r=>setTimeout(r,ms));

鉴于这个功能的实用性,Box3内置了sleep函数,用法和功能和上面的例子相同

 

其实Promise的功能远不止于此

下面来看一个例子:玩家足够5人后提示开始游戏

喵sync的做法

let players = 0;

world.onPlayerJoin(()=>{players++});
let tickToken = world.onTick(()=>{
  if(players>5){
    tickToken.cancel();  // 用来取消onTick监听
    world.say("开始游戏")
  }
})

有async的做法

(async ()=>{
  for(x=0;x<5;){
    await world.nextPlayerJoin();
  }
  world.say("游戏开始")
})()

对比两种写法,显然第二种更为简洁,这就是async的一个优点"结构化处理事件",这样你就可以更简单地控制逻辑结构,避免专注于事件处理,这对于有游戏规则和游戏计时的代码非常有用

 

但是使用async也有需要注意的地方,async函数会在主进程中立即执行,也就是说上下并列的两个async函数会一起执行,比如下面这个例子

(async()=>{
  for(x=0;x<5;x++){await sleep(100);console.log("hi")}
})(); // 这里必须有分号
(async()=>{
  for(x=0;x<5;x++){await sleep(100);console.log("hello")}
})();

乍眼看去应该是先有几个hi,再有几个hello,可是实际运行起来却是hi和hello同时出现,这是因为这两个async函数是同时执行的,而每到await的时候,程序会暂停当前代码块的执行,去处理其他代码块(异步执行),所以会造成这样的结果

再看这个例子

(async()=>{   for(x=0;x<5;){} })();
(async()=>{   for(x=0;x<5;x++){console.log("hello")} })();

正常来讲,两个async一起执行,第一个没有输出,第二个应该会看到5个hello,但是运次许久都看不见hello,最后还会因为运行超时而被阻断。这是因为,只有在程序空闲的时候才会处理其他代码,而在第一个for循环中,因为x永远不能大于5,所以会卡住,导致程序"太忙",就不能处理其他代码了。所以大家在async中有耗时较长的运算时,尽量在适当的地方加上await sleep(1); 甚至 0.1,这样可以让其他代码执行,并且对运算效率的影响也不是很大。

 

这个高级技巧,你get了吗~


回复

上一页1 页 / 共 1下一页
AlcalaAlcala

哇sofa

点赞0


评论


SKQASKQA

额.

点赞0


评论


Chengxuyee_已退Chengxuyee_已退

还是想用sleep()

点赞0


评论


幻想一笑而过幻想一笑而过

火钳刘明

点赞0


评论


BSS梅者如西BSS梅者如西

喵,shy喵

点赞0


评论


BSS梅者如西BSS梅者如西

shy为啥最近频繁诈尸()

点赞1


评论


天翼ipg音乐天翼ipg音乐

111

点赞0


评论


研岩研岩

emotion_编程猫_搓头

点赞0


评论


战神_小火熊_光影轮回战神_小火熊_光影轮回

nb……

点赞0


评论


豆子豆子豆子豆子

emotion_编程猫_点赞

点赞0


评论


话说像返回量是布尔值的怎么用啊???

点赞0


评论


神岛吉吉喵神岛吉吉喵

emotion_编程猫_点赞

点赞0


评论


我是曲奇饼干我是曲奇饼干

喵sync

点赞0


评论


萧弦_萧弦_

??真就自动顶帖呗??

点赞0


评论


wrmdcxywrmdcxy

你的帖子被选中为测试帖。

center_image

点赞0


评论


平凡的第五玩家平凡的第五玩家

GITHUB存储库怎么创建文件夹来着

点赞0


评论


a_b_c_a_b_c_

https://box3.codemao.cn/e/c47d6457f410c3d5eda5

点赞0


评论


1086DRRalsei1086DRRalsei

用await sleep(1000)它不香吗(滑稽)

点赞0


评论