暂且讨论睡眠功能的效用,我只想知道(更有可能确认)Javascript中不可能有睡眠功能。
但当然我需要限制用于构建睡眠功能的资源。睡眠功能的含义满足以下条件:
语法必须简单:
/*doing something before sleep*/
sleep(int milliseconds);//completely pause the execution
/*doing something after sleep*/
我注意到我们可以使用"从其他线程获取更新"的技巧,例如使用new Date()
来检查终止循环的条件。所以任何类似的行为,即让线程进入由布尔变量控制的循环,并且布尔变量取决于某些其他线程的信息,例如,系统时间,服务器时间等,应该超出讨论的范围。因此,强加的约束应该是:仅使用Javascript的本机功能构建睡眠功能,这意味着时间的唯一资源基本上是setTimeout()
。
嗯,直觉上我觉得构建这样的函数是不可能的,因为单个线程只能通过循环暂停。但是,使用单线程执行时,无法在循环内更改布尔变量(这决定了退出循环的条件)。
但这显然只是一种感觉,所以我只是想知道,有没有讨论JS中这种功能的实际可能性?
答案 0 :(得分:1)
这不是一个好问题。 JavaScript 是类似语言的通用词。想象(甚至创建)具有sleep
函数的JavaScript实现非常简单(尽管无用)。这是一回事。
其次setTimeout
确实使用系统/服务器时间。你不能没有另一个。所以你的问题的答案是:是的,只是循环时间检查(再次:完全没用)。你的第二个限制是不现实的。
真正的限制应该是:不使用100%CPU的睡眠。在这种情况下,任何主要的JS实现都不可能。
所有这些都是在同步睡眠的背景下。但是setTimeout
是睡眠。它只是异步的。为什么这是一个问题?
答案 1 :(得分:1)
使用Promise和新的ES6生成器,这几乎可以完成。看起来像这样:
sync(function* (){
yield sleep(3000);
console.log('more sleep');
yield sleep(3000);
console.log('done sleeping');
});
完整代码:
function sync(generator){
var _generator = generator();
function done(){
var result = _generator.next().value;
if(result instanceof Promise){
result.then(done);
}
}
done();
}
sync(function* (){
yield sleep(3000);
console.log('more sleep');
yield sleep(3000);
console.log('done sleeping');
});
function sleep(ms){
return new Promise(function(res, rej){
setTimeout(res, ms);
});
}
JsFiddle demo。 (浏览器必须support JS Harmony才能实现此功能)
这实际上并不阻止Javascript事件循环/线程。它只是暂停执行传递给sync
的函数,并在超时完成后恢复它。
答案 2 :(得分:1)
@levi:你的例子现在是“原生的”Javascript :)至少如果它被定义为ECMAscript 2015,那本周终于被批准了,包括函数*,yield *和yield语句。
这里我将展示如何从多个级别的函数调用 sleep()。这个例子用THREE.js绘制一个树(就像眼睛糖果一样),并使用sleep(),这样你就可以在构建时看到它。旋转场景,相机,灯光,着色器和渲染循环的三个东西隐藏在setup3D()中。
所有可以暂停的函数必须定义为GeneratorFunctions,简单地写为函数*。首次调用时,它返回一个GeneratorObject,但不执行该函数的主体。它开始“睡觉”可以这么说。 .next()方法将开始执行主体,直到下一个yield语句。
函数* sleep()设置wakup调用的计时器,然后产生(暂停)直到wakeup执行.next()。当它从yield中唤醒时,它会像正常函数一样返回,所有本地数据都保持不变。
主函数* drawTree()绘制一个分支,暂停,然后分成3个部分,每个部分是一个新的较小的树。一棵很小的树只是一片叶子。 GeneratorFunction可以使用yield *将执行委托给另一个GeneratorFunction。它的工作方式就像一个简单的函数调用,但它可以暂停产生。
site = Setup3D(20);
sleepingTask = new drawTree(site.center, 7);
sleepingTask.next();
function* sleep(delay, task) {
task = task || sleepingTask;
var wakeup = function() {task.next()};
setTimeout(wakeup, delay*1000);
yield;
}
function* drawTree(root, size) {
if (size<2) drawLeaf(root)
else yield* drawBranch(root, size);
}
function* drawBranch(stem, size) {
stem.add(new THREE.Mesh(new THREE.CylinderGeometry(size/12, size/10, size),
site.color(0.5,0.3,0.2)) .translateY(size/2));
stem.add(new THREE.Mesh(new THREE.SphereGeometry(size/12),
site.color(0.5,0.3,0.2)) .translateY(size));
for (var r of [1, 3, 5]) {
var newStem = new THREE.Object3D();
stem.add(newStem.translateY(size).rotateY(r).rotateX((20-r)/20));
yield* sleep(0.1);
yield* drawTree(newStem, size*(70+r)/100);
}
}
function drawLeaf(stem) {
stem.add(new THREE.Mesh(new THREE.CylinderGeometry(0, 0.1, 3),
site.color(0,1,0)) .rotateX(0.3).translateY(1.5));
stem.add(new THREE.Mesh(new THREE.CircleGeometry(1),
site.color(0,1,0)) .rotateX(0.3).translateY(2));
}
使用sleep()也可以使用多任务处理。这里drawLeaf()被扩展,因此每个叶子成为一个单独的任务。每片叶子都有自己的生命,它会生长,坐下,褪色和落下。叶子的GeneratorObject被添加到词干对象,并被用作第二个睡眠参数,因此回调知道要唤醒谁。函数* leafLife()是从drawLeaf()开始的,但drawLeaf()本身可以定义为一个简单的函数,因为它不使用yield或yield *。
function drawLeaf(stem) {
stem.add(new THREE.Mesh(new THREE.CylinderGeometry(0, 0.02, 0.6),
site.color(0,1,0)) .rotateX(0.3).translateY(0.3));
stem.add(new THREE.Mesh(new THREE.CircleGeometry(1/5),
site.color(0,1,0)) .rotateX(0.3).translateY(2/5));
stem.leafTask = new leafLife(stem);
stem.leafTask.next();
}
function* leafLife(stem) {
for (var i=0; i++<9;) {
yield* sleep(0.5, stem.leafTask);
stem.scale.multiplyScalar(1.2);
}
yield* sleep(10 + 30*Math.random(), task);
for (var i=0; i++<25;) {
yield* sleep(1, stem.leafTask);
stem.children[1].material.color.setRGB(i/25, 1-i/40, 0);
}
for (var i=0; i++<100;) {
yield* sleep(0.05, stem.leafTask);
stem.translateY(-2).rotateX(0.3).rotateY(0.5);
}
stem.visible = false;
}
完整代码,以防您想尝试自己:
site = Setup3D(20);
sleepingTask = new drawTree(site.center, 7);
sleepingTask.next();
function* sleep(delay, task) {
task = task || sleepingTask;
var wakeup = function() {
task.next()
};
setTimeout(wakeup, delay * 1000);
yield;
}
function* drawTree(root, size) {
if (size < 2) drawLeaf(root)
else yield* drawBranch(root, size);
}
function* drawBranch(stem, size) {
stem.add(new THREE.Mesh(new THREE.CylinderGeometry(size / 12, size / 10, size),
site.color(0.5, 0.3, 0.2)).translateY(size / 2));
stem.add(new THREE.Mesh(new THREE.SphereGeometry(size / 12),
site.color(0.5, 0.3, 0.2)).translateY(size));
for (var r of[1, 3, 5]) {
var newStem = new THREE.Object3D();
stem.add(newStem.translateY(size).rotateY(r).rotateX((20 - r) / 20));
yield* sleep(0.1);
yield* drawTree(newStem, size * (70 + r) / 100);
}
}
function drawLeaf(stem) {
stem.add(new THREE.Mesh(new THREE.CylinderGeometry(0, 0.02, 0.6),
site.color(0, 1, 0)).rotateX(0.3).translateY(0.3));
stem.add(new THREE.Mesh(new THREE.CircleGeometry(1 / 5),
site.color(0, 1, 0)).rotateX(0.3).translateY(2 / 5));
stem.leafTask = new leafLife(stem);
stem.leafTask.next();
}
function* leafLife(stem) {
var i, task = stem.leafTask;
for (i = 0; i++ < 9;) {
yield* sleep(0.5, task);
stem.scale.multiplyScalar(1.2);
}
yield* sleep(10 + 30 * Math.random(), task);
for (i = 0; i++ < 25;) {
yield* sleep(1, task);
stem.children[1].material.color.setRGB(i / 25, 1 - i / 40, 0);
}
for (i = 0; i++ < 100;) {
yield* sleep(0.05, task);
stem.translateY(-2).rotateX(0.3).rotateY(0.5);
}
stem.visible = false;
}
function Setup3D(rotationsPerMinute) {
var p = {};
p.scene = new THREE.Scene();
p.camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
p.camera.position.z = 50;
p.camera.position.y = 30;
p.renderer = new THREE.WebGLRenderer({
alpha: true,
antialias: true
});
p.renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(p.renderer.domElement);
var directionalLight = new THREE.DirectionalLight(0xffffaa, 0.7);
directionalLight.position.set(-1, 2, 1);
p.scene.add(directionalLight);
p.scene.add(new THREE.AmbientLight(0x9999ff));
p.center = new THREE.Object3D();
p.scene.add(p.center);
p.color = function(r, g, b) {
return new THREE.MeshLambertMaterial({
color: new THREE.Color(r, g, b),
vertexColors: THREE.FaceColors,
side: THREE.DoubleSide
});
};
var render = function() {
requestAnimationFrame(render);
p.renderer.setSize( window.innerWidth, window.innerHeight );
p.scene.rotateY(rotationsPerMinute / 60 / 60);
p.renderer.render(p.scene, p.camera);
};
render();
return p;
}
body {
margin: 0;
overflow: hidden;
background-color: #a0a0f0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r71/three.min.js"></script>
答案 3 :(得分:0)
简单地说 - 不,你不可能有一个全球性的睡眠&#34;样式函数将阻止Javascript在浏览器窗口中运行。
好的,这是简单的答案,更复杂的是它完全取决于你想要实现的目标。你实现可以模拟睡眠的方法就是拥有一个运行主逻辑的函数,它使用setTimeout调用自身(你需要记录一个数据以确保你的时间是正确的),然后你可以延迟在睡眠期间自行调用。
然而,这是纯粹的情境,你必须确保将所有逻辑编码到单个函数中,因为其他东西可以并行运行。