可以使用生成器函数来代替承诺管理吗? [英] Is it ok to use a generator function to replace a promise management?

查看:49
本文介绍了可以使用生成器函数来代替承诺管理吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

对于此代码示例,您必须想象一下移动机器人时的一系列动画(向左/向右移动,前进)

For this code example, you have to imagine a series of animations on moving a robot (move left / right, go forward)

实际上,它是一个动画更为复杂的网站(加载ajax,加载图像,多个动画等).我目前以承诺来管理,但是随着网站的发展,这部分的代码变成了spagettti的菜.

In reality it is a site with more complicated animations (loading ajax, loading images, multiple animations, etc ..) that I currently manage with promises, but as the site evolves, the code of this part becomes a dish of spagettti.

这是我第一次做这样的事情,我想知道这是否真的是个好主意,因为这种做事方式对我来说真的很奇怪.
我的印象是,我最终会发现自己遇到无法解决的问题.
无论如何,我当前的网站变成了一场噩梦,因为我必须更改某些动画,添加新的动画...

This is the first time I do something like this, and I wonder if this is really a good idea, because this way of doing things seems really strange for me.
I have the impression that I will eventually find myself with insoluble problems.
In any case my current site becomes a real nightmare, because I have to change some animations, add new ones ...

此示例代码看起来正确吗?
我应该在那改变一些东西吗

Does this sample code look correct?
Should I change something there?

const Root   = document.documentElement
  ,   gRoot  = getComputedStyle(Root)
  ,   moving = [ {T:-30,L:0},  {T:0,L:+30}, {T:+30,L:0}, {T:0,L:-30} ]
  ;
var RotateDeg = 0
  , RotateMov = 0
  , posT      = parseInt(gRoot.getPropertyValue('--PosT'))
  , posL      = parseInt(gRoot.getPropertyValue('--PosL'))
  ;
function F_1() // move forward
  {
  posT += moving[RotateMov].T
  posL += moving[RotateMov].L

  Root.style.setProperty('--PosT', posT + "px")
  Root.style.setProperty('--PosL', posL + "px")
  }
function T_L() // turn Left
  {
  RotateMov = (RotateMov +3) %4
  RotateDeg -=90
  Root.style.setProperty('--turn', RotateDeg + "deg")
  }
function T_R() // turn Right
  {
  RotateMov = (RotateMov +1) %4
  RotateDeg +=90
  Root.style.setProperty('--turn', RotateDeg + "deg")
  }
function R_0() // rotate to zero
  {
  RotateMov = 0
  RotateDeg = 0
  Root.style.setProperty('--turn', RotateDeg + "deg")
  }
function disableButtons(OnOff)
  {
  Bt_Tab_A.disabled = OnOff
  Bt_Tab_B.disabled = OnOff
  }
function* Sequence(Tab_fct)
  {
  for( let fct of Tab_fct) yield fct
  }

var iterator = Sequence([])

function nextSequence()
  {
  let command = iterator.next()
  if (!command.done) command.value()
  else disableButtons(false)
  }

Bt_Tab_A.onclick=_=>
  { 
  disableButtons(true)
  iterator = Sequence( [ F_1, T_L, F_1, T_R, F_1, T_R, F_1, F_1, T_R, F_1, F_1, T_R, F_1, R_0  ] )
  nextSequence()
  }
Bt_Tab_B.onclick=_=>
  { 
  disableButtons(true)
  iterator = Sequence( [ T_L, F_1, T_R, F_1, T_R, F_1, T_R, F_1, R_0 ] )
  nextSequence()
  }
robot.addEventListener('transitionend',  nextSequence )

:root {
  --turn  : 0deg;
  --PosT  : 110px;
  --PosL  : 90px;
}
#robot {
  font-size   : 16px;
  width       : 30px;
  height      : 30px;
  background-color: aqua;
  text-align  : center;
  line-height : 1.8em;
  transition  : all .5s linear;
  transform   : rotate( var(--turn) );
  position:fixed;
  top : var(--PosT);
  left: var(--PosL);
}

<div id="robot">R</div>

<button id="Bt_Tab_A"> Sequence A</button>
<button id="Bt_Tab_B"> Sequence B</button>

欢迎提供提示和建议;)

Tips and advice are welcome;)

推荐答案

在您的情况下,我认为Promise是必经之路.

In your case, I feel like Promise is the way to go.

以我自己的经验法则(请谨慎对待):

In my own rule of thumb (beware, it's opinionated):

  • 当需要顺序执行时,请使用promise.
  • 当需要生成结果时,请使用生成器功能.
  • 时,请使用.

在您的代码段中,您要做的就是让动画依次运行 ,而不是何时.

In your snippet, all you need to do is to have the animations run sequentially, not when you need it.

此外,您会注意到您有多个函数,这些函数取决于单个变量(即 iterator )的当前状态(结果).不好的是,当序列之间存在错误时,您将花费更多的时间和精力来调试这种情况,因为一个功能的一项更改可能会影响其他功能.乍一看,您还拥有一个全局的 transitionend 事件监听器.

Besides, you will notice you have multiple functions that are dependant on the current state (result) of a single variable (i.e., iterator). What's bad about this is that when there's a bug in between your sequence, you will take more time and effort to debug the situation because one change in one function might affect the other functions. You also have a global transitionend event listener that is quite consufing at the first glance.

简而言之,使用生成器函数,顺序操作的流程很难理解.

In short, using generator functions, the flow of sequential operations is hard to understand.

以下方法使用async/await.仅修改 Sequence nextSequence 方法(内部有注释说明).每个操作都包含在其自己的功能范围内.减少了对全局变量的依赖:

The approach below is using async/await. Only Sequence and nextSequence methods are modified (comment explanations inside). Every operation is contained within its own function scope. Reliant of global variables is reduced:

(对不起,我在编写代码时将代码格式化为我的代码样式)

const Root = document.documentElement;
const gRoot = window.getComputedStyle(Root);
const moving = [
  {
    T: -30,
    L: 0
  },
  {
    T: 0,
    L: +30
  },
  {
    T: +30,
    L: 0
  },
  {
    T: 0,
    L: -30
  }
];

let RotateDeg = 0;
let RotateMov = 0;
let posT = parseInt(gRoot.getPropertyValue('--PosT'));
let posL = parseInt(gRoot.getPropertyValue('--PosL'));

function F_1(){
  posT += moving[RotateMov].T;
  posL += moving[RotateMov].L;

  Root.style.setProperty('--PosT', posT + 'px');
  Root.style.setProperty('--PosL', posL + 'px');
}

function T_L(){
  RotateMov = (RotateMov + 3) % 4;
  RotateDeg -= 90;
  Root.style.setProperty('--turn', RotateDeg + 'deg');
}

function T_R(){
  RotateMov = (RotateMov + 1) % 4;
  RotateDeg += 90;
  Root.style.setProperty('--turn', RotateDeg + 'deg');
}

function R_0(){
  RotateMov = 0;
  RotateDeg = 0;
  Root.style.setProperty('--turn', RotateDeg + 'deg');
}

function disableButtons(OnOff){
  Bt_Tab_A.disabled = OnOff
  Bt_Tab_B.disabled = OnOff
}

async function Sequence(Tab_fct){
  // Disable buttons before start
  disableButtons(true);
  
  for (let fct of Tab_fct)
    // Run the animation one by one
    await nextSequence(fct);

  // Reenable buttons before end
  disableButtons(false);
}

function nextSequence(fct){
  return new Promise(res => {
    // Move event listener here so that they dont depend on a global one.
    // Use { once: true } to run this callback only once
    window.addEventListener('transitionend', res, { once: true });
    
    // Run the animation
    fct();
  })
}


Bt_Tab_A.onclick = () => {
  Sequence([F_1, T_L, F_1, T_R, F_1, T_R, F_1, F_1, T_R, F_1, F_1, T_R, F_1, R_0]);
}

Bt_Tab_B.onclick = () => {
  Sequence([ T_L, F_1, T_R, F_1, T_R, F_1, T_R, F_1, R_0 ]);
}

:root {
  --turn  : 0deg;
  --PosT  : 110px;
  --PosL  : 90px;
}
#robot {
  font-size   : 16px;
  width       : 30px;
  height      : 30px;
  background-color: aqua;
  text-align  : center;
  line-height : 1.8em;
  transition  : all .5s linear;
  transform   : rotate( var(--turn) );
  position:fixed;
  top : var(--PosT);
  left: var(--PosL);
}

<div id="robot">R</div>

<button id="Bt_Tab_A">Sequence A</button>
<button id="Bt_Tab_B">Sequence B</button>

这篇关于可以使用生成器函数来代替承诺管理吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆