你在unity 使用协程是用了IEnumerator 建立函数,其实就是js 的Generator
那我把setTimeout改为director.getScheduler().schedule()不就一定隔1帧咯?
不过我觉得用setTimeOut(0)应该也没问题,加到宏任务,当前帧应该就会执行了。
对的
个i值是一个闭包的结果
但协程核心是函数切片,把函数变成链,yield就是用来隔分链的节点,不需要有额外闭包
为啥我要在promise里面写循环啊,我做一个promise,setTimeout(n)以后去resolve,用函数返回出来,然后写个async函数,每循环await 这个函数不就行了?
wait(n : number): promise<void> {
return new Promise(() => {
setTimeout(resolve,n);
})
}
async loop() {
for (let i = 0; i < 10000; ++i) {
// do something
await wait(1);
}
}
不就和你那yield一样嘛。
实际上逻辑运行起来也没什么差别,用js原生的更容易理解点。
setTimeout(0) 是肯定不行的,因为若果你把帧下降你会发现还未到下一帧setTimout的回调已经执行
而且利用schedule这个方法你还要创建一堆回调,和临时函数,相返这个协程使用了Generator 方法,把函数转化成链执行,是不需要额外创建任何临时函数和回调。
其实为什么unity用c#的IEnumrator做他的coroutine,是因为c#本身的async/await用的线程池,多线程的,区别比较大。
js的async 是在主线程里面的,也是函数代码分片后变成callback的,其实触发分片用promise回调和用enumrator外部调度,在逻辑本身上是一样的。所以就没有弄couroutine类似的东西了。
我在unity也用 async/await,unitask就干这事的
你这样说,我可要怀疑你的js功底了
確實, 我判斷错了
js的协程Generators是es6的时候出来的,之前是没有的.js的异步底层是宏任务微任务,所以实现Generators也是基于这个事件循环,你把协程代码转成es5代码就可以发现它的实现原理
setTimeout(()=>{},0) 和 this.scheduleOnce(()=>{},0)是不一样的。前者只是把任务立刻放到任务队列里,当前帧的同步代码执行完立刻就执行了,不会等到引擎的下一帧。而后者是会延迟到下一帧执行的。
这个可以实现利用空闲时间完成耗时运算(平常一帧算不完)而不导致卡顿吗?
看你是想怎麽处理, 如果是 在while 或 for里, 使用yield字様就会执行到这就释放运算, 下一帧再继续, 记住函数名前加*号, 不然yield无法生效
控制在一帧时间快到的时候再停止而不是每帧跑一个yield
你这种想方法是不对的
协程的作用在于把一件事分多个帧去处理, 减少一帧的压力
比你原本你要1帧实例100个敌人, 那麽那一帧100%卡, 但是你可以分成每帧实例5个, 20帧实例完
那麽你for循还只需要 for(let i = 0 ;i < 100;++i){ if(i > 0 && i%5==0) yield };
当for执行完结后,协程就销毁了。
一帧差不多13毫秒,第一帧5毫秒渲染完了我让协程跑8毫秒,第二帧10毫秒渲染完了我让协程跑3毫秒。
这样
你协程函数里要执行多久是你自己写了甚麽代码
你可以这様 , yield是分隔线, 一个函数里可以有多个yield
function *f(){
//第一帧内容
let a = 0;
for(let i= 0 ;i < 100;++i){
a+=i;
}
yield //以上是第1帧运行,下面是第二帧
let b = 0;
for(let i= 0 ;i <10;++i){
b+=i;
}
//如果有第三帧要处理的东西再加yield, 否则不写或return
}
协程每次yield的时候检测此时是否为空闲时间,是则next,否则等下一帧渲染后的空闲时间,除非卡顿严重否则只用空闲时间跑协程
问题协程怎麽知道现在是否为空闲? 空闲的定义是甚麽?unity协程有实现你想要的"空闲"时处理吗?协程是主进程里执行的, 别把协程跟线程混淆
private *_test() {
let lastUpdate = cc.director._lastUpdate
let checkNext = function () {
if (performance.now() - lastUpdate > 1000 / cc.game.getFrameRate()) {
console.log("wait for next frame")
lastUpdate = cc.director._lastUpdate
return false
}
}
while (true) {
if (!checkNext()) {
yield;
}
}
}
Coroutine.instance.start(this, this._test);