我正在研究一种LED条带动画工具,它允许用户选择可同时运行的多种效果。每个效果都是(蓝鸟)的承诺。有一种run()
方法可以设置LED条的颜色。
所有承诺都使用delay
方法以固定的FPS运行。
run(mode) {
return this.setStripColor(this.color).delay(1 / this.fps).then(() => { this.run(1 / this.fps) })
}
// example of an effect
rainbowSweep() {
// ..
// magical unicorn code
// ..
return Promise.resolve().delay(1 / this.fps).then(() => {
this.rainbowSweep()
})
app.rainbowSweep()
app.run()
我可以使用某种数据结构来切换递归承诺吗?换句话说,我如何通知效果(递归承诺)以停止递归?
我在考虑一个包含所有承诺的数组。
但是当我不再在阵列中时,我不知道如何打破/解决递归承诺。我可以在return
之前检查一下承诺本身是否在数组内部,但我希望有一种更优雅的方式。
答案 0 :(得分:1)
让我们看一个简单的递归函数,它在高级别表达我们的程序
let RUNNING =
true
const main = async (elem, color = Color ()) =>
RUNNING
? delay (color, FPS)
.then (effect (color => setElemColor (elem, color)))
.then (color => main (elem, stepColor (color)))
: color
我们通过Color
,stepColor
和setElemColor
(以及其他人)做了一点一厢情愿的想法,让我们实现第一个
const Color = (r = 128, g = 128, b = 128) =>
({ r, g, b })
const stepColor = ({ r, g, b }, step = 8) =>
b < 255
? Color (r, g, b + step)
: g < 255
? Color (r, g + step, 0)
: r < 255
? Color (r + step, 0, 0)
: Color (0, 0, 0)
const setElemColor = (elem, { r, g, b }) =>
elem.style.backgroundColor = `rgb(${r}, ${g}, ${b})`
const c = new Color () // { r: 128, g: 128, b: 128 }
setpColor (c) // { r: 128, g: 128, b: 136 }
现在我们有了一种创造色彩的方法,然后获得了#34;下一个&#34;颜色,我们可以设置HTML元素的颜色
最后,我们写了助手delay
和effect
。 delay
将创建一个在ms
毫秒内解析的Promised值。 effect
用于具有副作用的函数(如设置HTML元素的属性)。而FPS
只是我们每秒帧数的常数
const delay = (x, ms) =>
new Promise (r => setTimeout (r, ms, x))
const effect = f => x =>
(f (x), x)
const FPS =
1000 / 30
要运行程序,只需使用input元素调用main
即可。因为它是一个异步程序,所以不要忘记处理成功的和错误情况。当程序最终停止时,将输出最后使用的颜色。
main (document.querySelector('#main'))
.then (console.log, console.error)
// => { Color r: 136, g: 8, b: 40 }
要停止该程序,只需随时设置RUNNING = false
// stop after 5 seconds
setTimeout (() => RUNNING = false, 5000)
这是一个有效的演示
const Color = (r = 128, g = 128, b = 128) =>
({ r, g, b })
const stepColor = ({ r, g, b }, step = 16) =>
b < 255
? Color (r, g, b + step)
: g < 255
? Color (r, g + step, 0)
: r < 255
? Color (r + step, 0, 0)
: Color (0, 0, 0)
const setElemColor = (elem, { r, g, b }) =>
elem.style.backgroundColor = `rgba(${r}, ${g}, ${b}, 1)`
const delay = (x, ms) =>
new Promise (r => setTimeout (r, ms, x))
const effect = f => x =>
(f (x), x)
const FPS =
1000 / 60
let RUNNING =
true
const main = async (elem, color = Color ()) =>
RUNNING
? delay (color, FPS)
.then (effect (color => setElemColor (elem, color)))
.then (color => main (elem, stepColor (color)))
: color
main (document.querySelector('#main'))
.then (console.log, console.error)
// => { r: 136, g: 8, b: 40 }
// stop after 5 seconds
setTimeout (() => RUNNING = false, 5000)
&#13;
#main {
width: 100px;
height: 100px;
background-color: rgb(128, 128, 128);
}
&#13;
<div id="main"></div>
<p>runs for 5 seconds...</p>
&#13;
答案 1 :(得分:0)
这是使用异步生成器的示例,其中可以使用return
或break
const o = {
value: new Map,
done: false,
async * gen(...props) {
while (!this.done) {
if (this.value.size && !this.done) {
try {
for (const [key, val] of this.value) {
// yield* [{key, value: await Promise.all([].concat(val).map(prop => typeof prop === "function" ? prop() : prop)).then(values => { this.value.delete(key); return values }).catch(err => {console.log(err); return {error:[key, err]}})}];
// does not interrupt immediately when `this.done` is set to `true`
for await (const value of [].concat(val).map(prop => Promise.resolve(typeof prop === "function" ? prop() : prop).then(prop => {console.log("prop:", prop); return prop}, err => {
console.error("caught at for await:", err); return err;
}))) {
console.log("value:", value);
if (key !== undefined && value !== undefined && !o.done)
yield * [{
key, value
}];
}
this.value.delete(key);
}
} catch (error) {
console.error("caught at async:", error);
throw error
}
} else {
// yield await `no value at ${new Date()}`;
this.done = true;
break;
}
}
}
};
(async() => {
let n = 0;
let caught, gen;
try {
gen = o.gen();
next = new Proxy(o.value.set.bind(o.value), {
apply(target, _, args) {
// console.log(args);
if (!o.done && args.length) {
Reflect.apply(target, null, args);
return gen.next().catch(err => {
throw err
})
};
if (!args.length && !o.done) return gen.next().catch(err => Promise.reject(err));
if (o.done) {
caught = "we're done here";
return gen.throw(caught).catch(err => {throw err})
};
}
});
await next(n, Promise.resolve(0)).catch(err => {
throw err
})
.then(({
value, done
}) => console.log(value, done));
await next(++n, Promise.resolve(1))
.catch(err => {
throw err
})
.then(({
value, done
}) => console.log(value, done));
await next(++n, [Promise.resolve(2)])
.catch(err => {
throw err
})
.then(({
value, done
}) => console.log(value, done));
await next(++n, [() => new Promise(r => setTimeout(r, 2000, 3))
, () => new Promise((_, reject) => setTimeout(_, 6000, 4))
, () => new Promise(r => setTimeout(r, 4000, 5))
]
)
.catch(err => {
throw err
})
.then(({
value, done
}) => console.log(value, done));
// o.done = true; // stop generator
while (o.value.size && !o.value.done) {
await next()
.catch(err => {
throw err
}).then(({
value, done
}) => {
console.log(value, done, o.value.size);
return
})
}
} catch (e) {
let message = await e;
if (message === "we're done here") {
console.error("err:", message);
} else {
console.error("caught at catch:", message);
throw new Error(message)
}
} finally {
console.log(gen);
if (caught) throw new Error(caught)
return {done: o.done, value: o.value.size}
}
})()
.catch(err => { console.error("ok caught:", err); return err})
.then(done => console.log("done:", done));
async function* gen(n) {
let i = 1;
while(true) {
let curr = yield await new Promise(r => setTimeout(r, 1000, i));
if (curr) {
i *= curr;
};
i *= 25;
if (i > 500 * 500) break;
}
}
async function read(n) {
const g = gen(n);
// while ( await g.next().then(({value, done}) => {console.log(value, done); return !done}) );
await g.next().then(({value, done}) => {console.log(value, done); return !done});
await g.next(100).then(({value, done}) => {console.log(value, done); return !done});
await g.next().then(({value, done}) => {console.log(value, done); return !done});
await g.next().then(({value, done}) => {console.log(value, done); return !done});
}
read(5)
答案 2 :(得分:0)
我可能会因为对你的问题的解释而偏离基础,但我认为你在所有递归完成后都试图解决最初的承诺。如果这就是您所要求的,您可以将初始承诺传递给每个递归调用。
我会在我的示例中使用您的示例代码,但是您错过了函数的右大括号,所以我不确定它应该是什么样子......这是概念的一般示例..
var recusion_counter = 0;
function myRecusivePromiseFunction(prm){
return new Promise(done=>{
recusion_counter++;
prm = prm || done;
if(recusion_counter >= 5) prm();
else myRecusivePromiseFunction(prm);
});
}
myRecusivePromiseFunction().then(()=>{
console.log(recusion_counter);
});