Вы можете использовать общий (общий) метод для чтения атрибута над данным MemberInfo
public static bool TryGetAttribute<T>(MemberInfo memberInfo, out T customAttribute) where T: Attribute {
var attributes = memberInfo.GetCustomAttributes(typeof(T), false).FirstOrDefault();
if (attributes == null) {
customAttribute = null;
return false;
}
customAttribute = (T)attributes;
return true;
}
Большинство ответов здесь ошибочны или, по крайней мере, устарели. Нет причин, по которым javascript должен быть однопоточным, и на самом деле это не так. Сегодня все основные браузеры поддерживают рабочих, до этого другие среды исполнения javascript, такие как Rhino и Node.js, поддерживали многопоточность.
«Javascript является однопоточным» не является правильным ответом. Например, выполнение функции сна внутри рабочего не блокирует какой-либо код, выполняемый в потоке пользовательского интерфейса.
В более новых средах выполнения, поддерживающих генераторы и производительность, можно было бы придать аналогичную функциональность функции сна в однопоточной среде:
// This is based on the latest ES6 drafts.
// js 1.7+ (SpiderMonkey/Firefox 2+) syntax is slightly different
// run code you want to sleep here (ommit star if using js 1.7)
function* main(){
for (var i = 0; i < 10; i++) {
// to sleep for 10 milliseconds 10 times in a row
yield 10;
}
yield 5;
console.log('I just slept 5 milliseconds!');
}
// resume the given generator after ms milliseconds
function resume(ms, generator){
setTimeout(function(){
// ommit .value if using js 1.7
var nextSleep = generator.next().value;
resume(nextSleep, generator);
}, ms);
}
// initialize generator and get first sleep for recursive function
var
generator = main(),
firstSleep = generator.next().value;
// initialize recursive resume function
resume(firstSleep, generator);
Эта имитация сна отличается от истинной функции сна, поскольку она не заблокировать поток. Это просто сахар поверх текущей функции javascript setTimeout. Этот тип функциональности был реализован в Task.js и должен работать сегодня в Firefox.
Хорошей альтернативой в некоторых ситуациях является отображение панели сообщений верхнего уровня, чтобы остановить взаимодействие с пользователем, а затем скрыть ее снова, когда вы получите ожидаемый результат (асинхронно). Это позволяет браузеру выполнять фоновые задачи, но приостанавливает рабочий процесс до тех пор, пока вы не вернете свой результат.
Метод объекта, который должен использовать метод «сна», такой как следующий:
function SomeObject() {
this.SomeProperty = "xxx";
return this;
}
SomeObject.prototype.SomeMethod = function () {
this.DoSomething1(arg1);
sleep(500);
this.DoSomething2(arg1);
}
Может быть почти переведен на:
function SomeObject() {
this.SomeProperty = "xxx";
return this;
}
SomeObject.prototype.SomeMethod = function (arg1) {
var self = this;
self.DoSomething1(arg1);
setTimeout(function () {
self.DoSomething2(arg1);
}, 500);
}
Разница в том, что операция SomeMethod возвращается до выполнения операции DoSomething2. Вызывающий "SomeMethod" не может зависеть от этого. Поскольку метод «Sleep» не существует, я использую более поздний метод и соответствующим образом проектирую свой код.
Надеюсь, это поможет.
Я перебираю решение в течение дня, но все еще думаю, как сохранить цепочку при использовании обратного вызова. Все знакомы с традиционным стилем программирования, в котором код выполняется построчно, синхронно. SetTimeout использует обратный вызов, поэтому следующая строка не ожидает его завершения. Это позволило мне подумать, как сделать его "синхронизированным", чтобы сделать функцию "сна".
Начиная с простой сопрограммы:
function coroutine() {
console.log('coroutine-1:start');
sleepFor(3000); //sleep for 3 seconds here
console.log('coroutine-2:complete');
}
Я хочу поспать 3 секунды посередине, но не хочу доминировать над всем потоком, поэтому сопрограмма должна выполняться другим потоком. Я рассматриваю Unity YieldInstruction и изменяю сопрограмму следующим образом:
function coroutine1() {
this.a = 100;
console.log('coroutine1-1:start');
return sleepFor(3000).yield; // sleep for 3 seconds here
console.log('coroutine1-2:complete');
this.a++;
}
var c1 = new coroutine1();
Объявите прототип sleepFor:
sleepFor = function(ms) {
var caller = arguments.callee.caller.toString();
var funcArgs = /\(([\s\S]*?)\)/gi.exec(caller)[1];
var args = arguments.callee.caller.arguments;
var funcBody = caller.replace(/^[\s\S]*?sleepFor[\s\S]*?yield;|}[\s;]*$/g,'');
var context = this;
setTimeout(function() {
new Function(funcArgs, funcBody).apply(context, args);
}, ms);
return this;
}
После запуска сопрограммы1 (я проверил в IE11 и Chrome49) вы увидите, что он спит 3 секунды между двумя операторами консоли. Он сохраняет коды так же красиво, как и традиционный стиль. Хитрость во сне для рутины. Он читает тело функции вызывающей стороны как строку и разбивает его на 2 части. Снимите верхнюю часть и создайте другую функцию нижней частью. После ожидания указанного количества миллисекунд он вызывает созданную функцию, применяя исходный контекст и аргументы. Для оригинального потока это закончится "возвращением" как обычно. За "доходность"? Он используется для сопоставления регулярных выражений. Это необходимо, но бесполезно.
1116 Это не на все 100% идеально, но, по крайней мере, оно достигает моей работы. Я должен упомянуть некоторые ограничения в использовании этой части кода. Поскольку код разбивается на 2 части, оператор return должен быть во внешнем, а не в любом цикле или {}. т.е.
function coroutine3() {
this.a = 100;
console.log('coroutine3-1:start');
if(true) {
return sleepFor(3000).yield;
} // <- raise exception here
console.log('coroutine3-2:complete');
this.a++;
}
Приведенные выше коды должны иметь проблему, поскольку закрывающая скобка не может существовать отдельно в созданной функции. Другое ограничение - все локальные переменные, объявленные как "var xxx = 123", не могут быть перенесены в следующую функцию. Вы должны использовать «this.xxx = 123» для достижения того же. Если у вашей функции есть аргументы, и они получили изменения, измененное значение также не может быть перенесено в следующую функцию.
function coroutine4(x) { // assume x=abc
var z = x;
x = 'def';
console.log('coroutine4-1:start' + z + x); //z=abc, x=def
return sleepFor(3000).yield;
console.log('coroutine4-2:' + z + x); //z=undefined, x=abc
}
Я бы представил еще один прототип функции: waitFor
waitFor = function(check, ms) {
var caller = arguments.callee.caller.toString();
var funcArgs = /\(([\s\S]*?)\)/gi.exec(caller)[1];
var args = arguments.callee.caller.arguments;
var funcBody = caller.replace(/^[\s\S]*?waitFor[\s\S]*?yield;|}[\s;]*$/g,'');
var context = this;
var thread = setInterval(function() {
if(check()) {
clearInterval(thread);
new Function(funcArgs, funcBody).apply(context, args);
}
}, ms?ms:100);
return this;
}
Он ожидает функцию «check», пока не вернет true. Он проверяет значение каждые 100 мс. Вы можете настроить его, передав дополнительный аргумент. Рассмотрим тестирование coroutine2:
function coroutine2(c) {
/* some codes here */
this.a = 1;
console.log('coroutine2-1:' + this.a++);
return sleepFor(500).yield;
/* next */
console.log('coroutine2-2:' + this.a++);
console.log('coroutine2-2:waitFor c.a>100:' + c.a);
return waitFor(function() {
return c.a>100;
}).yield;
/* the rest of code */
console.log('coroutine2-3:' + this.a++);
}
Также в красивом стиле, который мы любим до сих пор. На самом деле я ненавижу вложенный обратный вызов. Легко понять, что сопрограмма2 будет ждать завершения сопрограммы1. Интересно? Хорошо, затем запустите следующие коды:
this.a = 10;
console.log('outer-1:' + this.a++);
var c1 = new coroutine1();
var c2 = new coroutine2(c1);
console.log('outer-2:' + this.a++);
Вывод:
outer-1:10
coroutine1-1:start
coroutine2-1:1
outer-2:11
coroutine2-2:2
coroutine2-2:waitFor c.a>100:100
coroutine1-2:complete
coroutine2-3:3
Наружное завершение сразу же после инициализированных сопрограмм1 и сопрограмм2. Затем coroutine1 будет ждать 3000 мс. Coroutine2 войдет в шаг 2 после ожидания 500 мс. После этого он продолжит шаг 3, как только обнаружит значения coroutine1.a> 100.
Остерегайтесь того, что есть 3 контекста для хранения переменной «a». Один является внешним, значения которого равны 10 и 11. Другой находится в coroutine1, значения которого равны 100 и 101. Последний находится в coroutine2, значения которого равны 1,2 и 3. В coroutine2 он также ожидает ca, который приходит от coroutine1, пока его значение не станет больше 100. 3 контекста являются независимыми.
Весь код для копирования и вставки:
sleepFor = function(ms) {
var caller = arguments.callee.caller.toString();
var funcArgs = /\(([\s\S]*?)\)/gi.exec(caller)[1];
var args = arguments.callee.caller.arguments;
var funcBody = caller.replace(/^[\s\S]*?sleepFor[\s\S]*?yield;|}[\s;]*$/g,'');
var context = this;
setTimeout(function() {
new Function(funcArgs, funcBody).apply(context, args);
}, ms);
return this;
}
waitFor = function(check, ms) {
var caller = arguments.callee.caller.toString();
var funcArgs = /\(([\s\S]*?)\)/gi.exec(caller)[1];
var args = arguments.callee.caller.arguments;
var funcBody = caller.replace(/^[\s\S]*?waitFor[\s\S]*?yield;|}[\s;]*$/g,'');
var context = this;
var thread = setInterval(function() {
if(check()) {
clearInterval(thread);
new Function(funcArgs, funcBody).apply(context, args);
}
}, ms?ms:100);
return this;
}
function coroutine1() {
this.a = 100;
console.log('coroutine1-1:start');
return sleepFor(3000).yield;
console.log('coroutine1-2:complete');
this.a++;
}
function coroutine2(c) {
/* some codes here */
this.a = 1;
console.log('coroutine2-1:' + this.a++);
return sleepFor(500).yield;
/* next */
console.log('coroutine2-2:' + this.a++);
console.log('coroutine2-2:waitFor c.a>100:' + c.a);
return waitFor(function() {
return c.a>100;
}).yield;
/* the rest of code */
console.log('coroutine2-3:' + this.a++);
}
this.a = 10;
console.log('outer-1:' + this.a++);
var c1 = new coroutine1();
var c2 = new coroutine2(c1);
console.log('outer-2:' + this.a++);
Он протестирован в IE11 и Chrome49. Поскольку он использует arguments.callee, это может вызвать проблемы, если он работает в строгом режиме.
У меня был этот вопрос в течение долгого времени, и мне нужен был не тот ответ, который был здесь представлен. Эта функция ожидания вызывает синхронное ожидание, которое не связывает процессор. waitForIt отправляет ajax-запрос в любое место и устанавливает для флага асинхронности значение false. waitF делает то же самое с фреймом, а waitD делает то же самое с div. Ajax занимает около 100 мс, фрейм - около 25, а div - около 1. Функция ожидания использует все это в зависимости от того, сколько времени вы даете. Если это не заставило себя долго ждать, сделайте это снова. Мне это нужно при работе с несколькими асинхронными загрузочными элементами. В основном для «подождите, пока этот элемент существует». Вы можете поиграть с ним здесь https://jsfiddle.net/h2vm29ue/ Он просто использует то, что естественно ожидает браузер. Более длинная версия https://jsfiddle.net/5cov1p0z/32/ является более точной.
function waitForIt() {
var start = new Date();
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
//doesn't matter
}
};
xhttp.open("GET", "WaitForIt", false);
xhttp.send();
var end = new Date();
}
//
function waitF() {
var start = new Date();
var ifram = document.createElement('iframe');
ifram.id = 'ifram';
ifram.src = '';
var div = document.createElement('div');
div.id = 'timer';
document.body.appendChild(div);
document.getElementById('timer').appendChild(ifram);
document.getElementById('timer').removeChild(ifram);
document.body.removeChild(div);
var end = new Date();
return (end - start);
}
function waitD() {
var start = new Date();
var div = document.createElement('div');
div.id = 'timer';
document.body.appendChild(div);
div.click();
document.body.removeChild(div);
var end = new Date();
return (end - start);
}
function wait(time) {
var start = new Date();
var end = new Date();
while ((end - start < time)) {
if ((time - (end - start)) >= 200) {
waitForIt();
} else {
if ((time - (end - start)) >= 50) {
waitF();
} else {
waitD();
}
}
end = new Date();
}
return (end - start);
}
Старый вопрос 2009 года. Теперь в 2015 году возможно новое решение с генераторами, определенными в ECMAscript 2015 aka ES6. Он был одобрен в июне, но ранее он был реализован в Firefox и Chrome. Теперь функция сна может быть сделана не занятой, неблокируемой и вложенной в циклы и подфункции без зависания браузера. Нужен только чистый JavaScript, никаких библиотек или фреймворков.
В программе ниже показано, как можно создать sleep()
и runSleepyTask()
. Функция sleep()
является только оператором yield
. Это так просто, что на самом деле проще написать оператор yield
вместо вызова sleep()
, но тогда не было бы спящего слова :-) yield возвращает значение времени методу next()
внутри wakeup()
и ждет. Фактический «сон» сделан в wakeup()
с использованием старого доброго setTimeout()
. При обратном вызове метод next()
запускает оператор yield
для продолжения, и «волшебство» yield заключается в том, что все локальные переменные и весь стек вызовов вокруг него все еще не повреждены.
Функции, которые используют sleep () или yield, должны быть определены как генераторы. Это легко сделать, добавив звездочку к ключевому слову function*
. Выполнить генератор немного сложнее. При вызове с ключевым словом new
генератор возвращает объект с методом next()
, но тело генератора не выполняется (ключевое слово new
является необязательным и не имеет значения). Метод next()
запускает выполнение тела генератора, пока не встретится yield
. Функция обертки runSleepyTask()
запускает пинг-понг: next()
ожидает yield
, а yield
ждет next()
.
Еще один способ вызвать генератор - использовать ключевое слово yield*
, здесь он работает как простой вызов функции, но также включает возможность возврата к next()
.
Это все продемонстрировано на примере drawTree()
. Он рисует дерево с листьями на вращающейся трехмерной сцене. Дерево нарисовано в виде ствола с 3-мя частями вверху в разных направлениях. Затем каждая часть рисуется как другое, но меньшее дерево, вызывая drawTree()
рекурсивно после короткого сна. Очень маленькое дерево нарисовано только как лист.
Каждый лист имеет свою собственную жизнь в отдельной задаче, начатой с runSleepyTask()
. Он рождается, растет, сидит, увядает, падает и умирает в growLeaf()
. Скорость регулируется с помощью sleep()
. Это демонстрирует, как легко можно сделать многозадачность.
function* sleep(milliseconds) {yield milliseconds};
function runSleepyTask(task) {
(function wakeup() {
var result = task.next();
if (!result.done) setTimeout(wakeup, result.value);
})()
}
//////////////// written by Ole Middelboe /////////////////////////////
pen3D =setup3D();
var taskObject = new drawTree(pen3D.center, 5);
runSleepyTask(taskObject);
function* drawTree(root3D, size) {
if (size < 2) runSleepyTask(new growLeaf(root3D))
else {
pen3D.drawTrunk(root3D, size);
for (var p of [1, 3, 5]) {
var part3D = new pen3D.Thing;
root3D.add(part3D);
part3D.move(size).turn(p).tilt(1-p/20);
yield* sleep(50);
yield* drawTree(part3D, (0.7+p/40)*size);
}
}
}
function* growLeaf(stem3D) {
var leaf3D = pen3D.drawLeaf(stem3D);
for (var s=0;s++<15;) {yield* sleep(100); leaf3D.scale.multiplyScalar(1.1)}
yield* sleep( 1000 + 9000*Math.random() );
for (var c=0;c++<30;) {yield* sleep(200); leaf3D.skin.color.setRGB(c/30, 1-c/40, 0)}
for (var m=0;m++<90;) {yield* sleep( 50); leaf3D.turn(0.4).tilt(0.3).move(2)}
leaf3D.visible = false;
}
///////////////////////////////////////////////////////////////////////
function setup3D() {
var scene, camera, renderer, diretionalLight, pen3D;
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(75,
window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(0, 15, 20);
renderer = new THREE.WebGLRenderer({ alpha: true, antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
directionalLight = new THREE.DirectionalLight(0xffffaa, 0.7);
directionalLight.position.set(-1, 2, 1);
scene.add(directionalLight);
scene.add(new THREE.AmbientLight(0x9999ff));
(function render() {
requestAnimationFrame(render);
// renderer.setSize( window.innerWidth, window.innerHeight );
scene.rotateY(10/60/60);
renderer.render(scene, camera);
})();
window.addEventListener(
'resize',
function(){
renderer.setSize( window.innerWidth, window.innerHeight );
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
},
false
);
pen3D = {
drawTrunk: function(root, size) {
// root.skin = skin(0.5, 0.3, 0.2);
root.add(new THREE.Mesh(new THREE.CylinderGeometry(size/12, size/10, size, 16),
root.skin).translateY(size/2));
root.add(new THREE.Mesh(new THREE.SphereGeometry(size/12, 16),
root.skin).translateY(size));
return root;
},
drawLeaf: function(stem) {
stem.skin.color.setRGB(0, 1, 0);
stem.add(new THREE.Mesh(new THREE.CylinderGeometry(0, 0.02, 0.6),
stem.skin) .rotateX(0.3).translateY(0.3));
stem.add(new THREE.Mesh(new THREE.CircleGeometry(0.2),
stem.skin) .rotateX(0.3).translateY(0.4));
return stem;
},
Thing: function() {
THREE.Object3D.call(this);
this.skin = new THREE.MeshLambertMaterial({
color: new THREE.Color(0.5, 0.3, 0.2),
vertexColors: THREE.FaceColors,
side: THREE.DoubleSide
})
}
};
pen3D.Thing.prototype = Object.create(THREE.Object3D.prototype);
pen3D.Thing.prototype.tilt = pen3D.Thing.prototype.rotateX;
pen3D.Thing.prototype.turn = pen3D.Thing.prototype.rotateY;
pen3D.Thing.prototype.move = pen3D.Thing.prototype.translateY;
pen3D.center = new pen3D.Thing;
scene.add(pen3D.center);
return pen3D;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r71/three.min.js"></script>
3D-содержимое скрыто внутри setup3D () и включено только для того, чтобы сделать его менее скучным, чем console.log ( ). Кстати, ангелы измеряются в радианах.
Проверено на работу в Firefox и Chrome. Не реализовано в Internet Explore и iOS (iPad). Попробуйте запустить его самостоятельно.
После очередного прохода ответов я обнаружил, что Габриэль Ратенер сделал аналогичный ответ год назад: https://stackoverflow.com/a/24401317/5032384
Обновление 2019 года с использованием Atomics.wait
Должно работать в узле 9.3 или выше.
Мне нужен был довольно точный таймер в Node.js, и он прекрасно работает для этого. Однако кажется, что поддержка браузеров крайне ограничена.
let ms = 10000;
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
Провел несколько 10-секундных тестов таймера.
С setTimeout я получаю ошибку до 7000 микросекунд. (7 мс)
При использовании Atomics моя ошибка, по-видимому, не превышает 600 мкс. (0.6ms)
Это может сработать. Это работало для меня в c и javascript.
function sleep(time) {
var x = 0;
for(x = 0;x < time;x++) {/* Do nothing*/}
}
В JavaScript я переписываю каждую функцию, чтобы она могла завершиться как можно скорее. Вы хотите, чтобы браузер снова контролировал ситуацию, чтобы он мог вносить изменения в вашу модель DOM.
Каждый раз, когда я хотел спать в середине моей функции, я реорганизовывал использование setTimeout ()
.
] Я собираюсь отредактировать этот ответ, потому что считаю его полезным:
Печально известная функция сна, или задержка, в любом языке широко обсуждается. Некоторые скажут, что всегда должен быть сигнал или обратный вызов для запуска данной функции, другие будут утверждать, что иногда полезен произвольный момент задержки. Я говорю, что каждому свое, и одно правило никогда ничего не может диктовать в этой отрасли.
Написать функцию сна просто и сделать ее еще более удобной с помощью JavaScript Promises:
// sleep time expects milliseconds
function sleep (time) {
return new Promise((resolve) => setTimeout(resolve, time));
}
// Usage!
sleep(500).then(() => {
// Do something after the sleep!
});
Самое короткое решение без любых зависимостей:
await new Promise(resolve => setTimeout(resolve, 5000));