Вы можете использовать общий (общий) метод для чтения атрибута над данным 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;
}
Если вы используете jQuery, кто-то на самом деле создал плагин «delay», который является не более чем оболочкой для setTimeout:
// Delay Plugin for jQuery
// - http://www.evanbot.com
// - © 2008 Evan Byrne
jQuery.fn.delay = function(time,func){
this.each(function(){
setTimeout(func,time);
});
return this;
};
Затем вы можете просто использовать его в строке вызовов функций, как и ожидалось. :
$('#warning')
.addClass('highlight')
.delay(1000)
.removeClass('highlight');
Лучшее решение для того, чтобы все выглядело так, как того хочет большинство людей, - это использование анонимной функции:
alert('start');
var a = 'foo';
//lots of code
setTimeout(function(){ //Beginning of code that should run AFTER the timeout
alert(a);
//lots more code
},5000); // put the timeout here
Это, вероятно, самое близкое к тому, что просто делает то, что вы хотите.
Обратите внимание, если вам нужно несколько снов, это может привести к неприятным последствиям, и вам, возможно, придется пересмотреть свой дизайн.
Для браузеров, я согласен, что setTimeout и setInterval - это то, что нужно.
Но для серверного кода может потребоваться функция блокировки (например, чтобы вы могли эффективно синхронизировать потоки).
Если вы используете node.js и meteor, возможно, вы столкнулись с ограничениями использования setTimeout в волокне. Вот код для сна на стороне сервера.
var Fiber = require('fibers');
function sleep(ms) {
var fiber = Fiber.current;
setTimeout(function() {
fiber.run();
}, ms);
Fiber.yield();
}
Fiber(function() {
console.log('wait... ' + new Date);
sleep(1000);
console.log('ok... ' + new Date);
}).run();
console.log('back in main');
Недавно у меня был случай, когда код, следующий за обратным вызовом, выполнялся до выполнения обратного вызова из $.ui.popup
. Мое решение состояло в том, чтобы установить переменную перед рукой, называемой pause, установить ее в true, выполнить код $.ui.popup
, а затем получить код, который мне нужно было ждать через некоторый интервал. Пример:
var pause = false;
function something() {
if(edited && renamed) {
pause = true;
$.ui.popup({
//...
doneCallback: function() {
//...
pause = false;
}
//...
});
//...
}
var waitCode = setInterval(function(){
if(!pause) {
//... code I needed to wait on ...
clearInterval(waitCode);
}
},500);
}
Я согласен с другими авторами, занятый сон - это просто плохая идея.
Однако setTimeout не задерживает выполнение, он выполняет следующую строку функции сразу после истечения времени ожидания SET, а не после истечения времени ожидания, так что не выполняет ту же задачу, что и сон.
Способ сделать это - разбить вашу функцию на части до и после.
function doStuff()
{
//do some things
setTimeout(continueExecution, 10000) //wait ten seconds before continuing
}
function continueExecution()
{
//finish doing things after the pause
}
Убедитесь, что имена ваших функций по-прежнему точно описывают, что делает каждый фрагмент (IE GatherInputThenWait и CheckInput, а не funcPart1 и funcPart2)
Редактировать
Этот метод позволяет не выполнять выбранные вами строки кода до ПОСЛЕ вашего тайм-аута, в то же время возвращая управление обратно клиентскому ПК, чтобы выполнить все остальное, находящееся в очереди.
Дальнейшее редактирование
Как указано в комментариях, это абсолютно НЕ РАБОТАЕТ в цикле. Вы можете сделать какой-нибудь причудливый (безобразный) взлом, чтобы заставить его работать в цикле, но в целом это просто приведет к катастрофическому коду спагетти.
Я тоже искал решение для сна (не для производственного кода, только для dev / tests) и нашел эту статью:
http://narayanraman.blogspot.com/2005/12 /javascript-sleep-or-wait.html
... и вот еще одна ссылка с решениями на стороне клиента: http://www.devcheater.com/
Кроме того, когда вы звоните alert()
, ваш код также будет приостановлен, пока отображается предупреждение - нужно найти способ не отображать предупреждение, но получить тот же эффект. :)
Вот, пожалуйста. Как говорится в коде, не будьте плохим разработчиком и используйте это на веб-сайтах. Это служебная функция разработки.
// Basic sleep function based on ms.
// DO NOT USE ON PUBLIC FACING WEBSITES.
function sleep(ms) {
var unixtime_ms = new Date().getTime();
while(new Date().getTime() < unixtime_ms + ms) {}
}
function sleep(milliseconds) {
var start = new Date().getTime();
for (var i = 0; i < 1e7; i++) {
if ((new Date().getTime() - start) > milliseconds){
break;
}
}
}
Ради любви к $ DEITY, пожалуйста, не делайте функцию сна с занятым ожиданием. setTimeout
и setInterval
делают все, что вам нужно.
Функция для сна, использующая синхронный вызов, чтобы ОС могла это сделать. Используйте любую команду OS Sleep вам нравится. Это не занято ожиданием в смысле использования процессорного времени.
Я выбрал пинг по несуществующему адресу.
const cp = require('child_process');
function sleep(ms)
{
try{cp.execSync('ping 192.0.2.0 -n 1 -w '+ms);}
catch(err){}
}
Тест для проверки работоспособности
console.log(Date.now());
console.log(Date.now());
sleep(10000);
console.log(Date.now());
console.log(Date.now());
И некоторые результаты теста.
1491575275136
1491575275157
(и через 10 секунд)
1491575285075
1491575285076
Мне лично нравится простое:
function sleep(seconds){
var waitUntil = new Date().getTime() + seconds*1000;
while(new Date().getTime() < waitUntil) true;
}
тогда:
sleep(2); // Sleeps for 2 seconds
Я использую его все время, чтобы создать ложное время загрузки при создании сценариев в P5js
Только для debug / dev , я публикую это, если это кому-нибудь пригодится
Интересные вещи, в Firebug (и, вероятно, другие js-консоли), после нажатия Enter ничего не происходит, только после указанной продолжительности сна (...)
function sleepFor( sleepDuration ){
var now = new Date().getTime();
while(new Date().getTime() < now + sleepDuration){ /* do nothing */ }
}
Пример использования:
function sleepThenAct(){ sleepFor(2000); console.log("hello js sleep !"); }
В случае, если вам действительно нужен сон () только для того, чтобы что-то проверить. Но имейте в виду, что в большинстве случаев во время отладки браузер вылетает из браузера - возможно, именно поэтому он вам и нужен. В производственном режиме я закомментирую эту функцию.
function pauseBrowser(millis) {
var date = Date.now();
var curDate = null;
do {
curDate = Date.now();
} while (curDate-date < millis);
}
Не используйте новую функцию Date () в цикле, если только вы не хотите тратить впустую память, вычислительную мощность, батарею и, возможно, время жизни вашего устройства.
Это на самом деле не очень хорошая идея, если сделать что-то подобное, можно было бы заморозить всю страницу, пока система ждала возврата вашей функции.
Используйте три функции:
setInterval
для запуска цикла clearInterval
для остановки цикла, а затем вызывает setTimeout
в сон, и, наконец, вызовы внутри setTimeout
в качестве обратного вызова для перезапуска цикла. clearInterval
после достижения максимального числа
var foo = {};
function main()
{
'use strict';
/*Initialize global state*/
foo.bar = foo.bar || 0;
/* Initialize timer */
foo.bop = setInterval(foo.baz, 1000);
}
sleep =
function(timer)
{
'use strict';
clearInterval(timer);
timer = setTimeout(function(){main()}, 5000);
};
foo.baz =
function()
{
'use strict';
/* Update state */
foo.bar = Number(foo.bar + 1) || 0;
/* Log state */
console.log(foo.bar);
/* Check state and stop at 10 */
(foo.bar === 5) && sleep(foo.bop);
(foo.bar === 10) && clearInterval(foo.bop);
};
main();
Ссылки
Я могу понять назначение функции сна, если вам приходится иметь дело с синхронным выполнением. Функции setInterval и setTimeout создают поток параллельного выполнения, который возвращает последовательность выполнения обратно в основную программу, что неэффективно, если вам приходится ждать заданного результата. Конечно, можно использовать события и обработчики, но в некоторых случаях это не то, что предназначено.
Если вы исправляете функцию сна, подобную этой
var sleep = function(period, decision, callback){
var interval = setInterval(function(){
if (decision()) {
interval = clearInterval(interval);
callback();
}
}, period);
}
, и у вас есть асинхронная функция для многократного вызова
var xhr = function(url, callback){
// make ajax request
// call callback when request fulfills
}
И вы настраиваете свой проект следующим образом:
var ready = false;
function xhr1(){
xhr(url1, function(){ ready = true;});
}
function xhr2(){
xhr(url2, function(){ ready = true; });
}
function xhr3(){
xhr(url3, function(){ ready = true; });
}
Тогда вы можете сделать это:
xhr1();
sleep(100, function(){ return done; }, xhr2);
sleep(100, function(){ return done; }, xhr3);
sleep(100, function(){ return done; }, function(){
// do more
});
Вместо бесконечного отступа обратного вызова, как это:
xhr(url1, function(){
xhr2(url2, function(){
xhr3(url3, function(){
// do more
});
});
});
Вот способ спать в .hta
сценарии, так что когда сценарий просыпается, он выполняет следующую команду в последовательности, как это необходимо в цикле. Это настоящий сон; он не удерживает процессор во время сна. Например. процессор может загружать и отображать страницы во время сна.
Только один раз, в начале кода, перейдите
var WSHShell = new ActiveXObject ("WScript.Shell");
Для сна, например, 1 секунда = 1000 миллисекунд, выполните оператор
WSHShell.Run ('Sleep.js 1000', 3, true);
В том же каталоге, что и скрипт, находится файл Sleep.js
, который содержит следующую строку:
WScript.Sleep (WScript.Arguments (0));
(Осторожно ; 0
в скобках, а не в скобках.) Последняя строка, которая фактически выполняет спящий режим. Аргумент true
в предыдущем фрагменте делает вызов синхронным. Кажется, что 3
в предыдущем аргументе не имеет никакого эффекта, но вам нужен некоторый аргумент, чтобы true
был третьим аргументом. Microsoft говорит: «Объект WScript ... никогда не нужно создавать, прежде чем вызывать его свойства и методы, и он всегда доступен из любого файла сценария», но это не так. Он доступен в автономном файле .js
, таком как приведенный выше, но, очевидно, не в файле .js
, используемом файлом .hta
, поэтому он должен быть в отдельном файле, который вызывается, как указано выше.
Для конкретного случая, когда требуется разметить набор вызовов, выполняемых циклом, вы можете использовать что-то вроде приведенного ниже кода с прототипом. Без прототипа вы можете заменить функцию задержки на setTimeout.
function itemHandler(item)
{
alert(item);
}
var itemSet = ['a','b','c'];
// Each call to itemHandler will execute
// 1 second apart
for(var i=0; i<itemSet.length; i++)
{
var secondsUntilExecution = i;
itemHandler.delay(secondsUntilExecution, item)
}
Если вам нравится совет, чтобы не потерять производительность. setTimeout
ваш ожидаемый sleep
. Однако, если вы хотите использовать синтаксис, в котором код «делится на середину» на sleep
, мы можем сделать:
sleep=function(tm,fn){
window.setTimeout(fn,tm);
}
затем выполнить следующие функции:
var fnBeforeSleep=function(){
//All codes before sleep
}
var fnAfterSleep=function(){
//All codes after sleep
}
Тогда :
fnBeforeSleep();
sleep(2000,
fnAfterSleep);
fnBeforeSleep();
sleep(2000);
fnAfterSleep();
(См. обновленный ответ за 2016 год )
Я думаю, что вполне разумно хотеть выполнить действие, подождать, а затем выполнить другое действие. Если вы привыкли писать на многопоточных языках, у вас, вероятно, есть идея дать выполнение за определенное время, пока ваш поток не проснется.
Проблема здесь в том, что JavaScript является однопоточной моделью, основанной на событиях. Хотя в конкретном случае было бы неплохо подождать всего несколько секунд весь двигатель, в целом это плохая практика. Предположим, я хотел использовать ваши функции, когда писал свои собственные? Когда я вызывал твой метод, все мои методы зависали. Если JavaScript может каким-то образом сохранить контекст выполнения вашей функции, сохранить его где-нибудь, затем вернуть обратно и продолжить позже, тогда может произойти сон, но это в основном будет потоковым.
Таким образом, вы в значительной степени застряли на том, что предлагали другие, - вам нужно разбить код на несколько функций.
Тогда ваш вопрос немного ошибочен. Нельзя спать так, как вы хотите, и вы не должны следовать предложенному вами решению.
Добавление двух моих битов. Я нуждался в занятом ожидании для целей тестирования. Я не хотел разбивать код, так как это было бы много работы, поэтому простой for сделал это для меня.
for (var i=0;i<1000000;i++){
//waiting
}
Я не вижу никаких недостатков в этом, и это помогло мне.
Код, взятый из этой ссылки , не замораживает комп. Но это работает только на ff.
/**
* Netscape compatible WaitForDelay function.
* You can use it as an alternative to Thread.Sleep() in any major programming language
* that support it while JavaScript it self doesn't have any built-in function to do such a thing.
* parameters:
* (Number) delay in millisecond
*/
function nsWaitForDelay(delay) {
/**
* Just uncomment this code if you're building an extention for Firefox.
* Since FF3, we'll have to ask for user permission to execute XPCOM objects.
*/
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
// Get the current thread.
var thread = Components.classes["@mozilla.org/thread-manager;1"].getService(Components.interfaces.nsIThreadManager).currentThread;
// Create an inner property to be used later as a notifier.
this.delayed = true;
/* Call JavaScript setTimeout function
* to execute this.delayed = false
* after it finish.
*/
setTimeout("this.delayed = false;", delay);
/**
* Keep looping until this.delayed = false
*/
while (this.delayed) {
/**
* This code will not freeze your browser as it's documented in here:
* https://developer.mozilla.org/en/Code_snippets/Threads#Waiting_for_a_background_task_to_complete
*/
thread.processNextEvent(true);
}
}
Это можно сделать с помощью метода сна Java. Я протестировал его в FF и IE, и он не блокирует компьютер, не проверяет ресурсы и не вызывает бесконечные попадания на сервер. Мне кажется, это чистое решение.
Сначала вы должны загрузить Java на страницу и сделать ее методы доступными. Для этого я сделал следующее:
<html>
<head>
<script type="text/javascript">
function load() {
var appletRef = document.getElementById("app");
window.java = appletRef.Packages.java;
} // endfunction
</script>
<body onLoad="load()">
<embed id="app" code="java.applet.Applet" type="application/x-java-applet" MAYSCRIPT="true" width="0" height="0" />
Затем все, что вам нужно сделать, когда вы хотите безболезненную паузу в вашем JS, это:
java.lang.Thread.sleep(xxx)
Где xxx - время в миллисекундах. , В моем случае (для обоснования) это было частью выполнения внутреннего заказа в очень маленькой компании, и мне нужно было распечатать счет, который должен был быть загружен с сервера. Я сделал это, загрузив счет (как веб-страницу) в iFrame, а затем распечатал iFrame. Конечно, мне пришлось ждать полной загрузки страницы, прежде чем я смог распечатать, поэтому JS пришлось сделать паузу. Я достиг этого, заставив страницу накладной (в iFrame) изменить скрытое поле формы на родительской странице с помощью события onLoad. И код на родительской странице для печати счета-фактуры выглядел следующим образом (несущественные части вырезаны для ясности):
var isReady = eval('document.batchForm.ready');
isReady.value=0;
frames['rpc_frame'].location.href=url;
while (isReady.value==0) {
java.lang.Thread.sleep(250);
} // endwhile
window.frames['rpc_frame'].focus();
window.frames['rpc_frame'].print();
Таким образом, пользователь нажимает кнопку, скрипт загружает страницу счета, затем ждет, проверяя каждый четверть секунды, чтобы увидеть, закончилась ли загрузка страницы счета-фактуры, а затем всплывающее диалоговое окно печати, чтобы пользователь мог отправить ее на принтер. Что и требовалось доказать.
Начиная с узла 7.6 , вы можете объединить функцию promisify
из модуля utils с setTimeout
.
const sleep = require('util').promisify(setTimeout)
async function main() {
console.time("Slept for")
await sleep(3000)
console.timeEnd("Slept for")
}
main()
async function asyncGenerator() {
while (goOn) {
var fileList = await listFiles(nextPageToken);
await sleep(3000)
var parents = await requestParents(fileList);
}
}
Вы не можете так спать в JavaScript, или, скорее, не должны. Запуск цикла sleep или while приведет к зависанию браузера пользователя до завершения цикла.
Используйте таймер, как указано в ссылке, на которую вы ссылались.
Если вы используете node.js, вы можете взглянуть на fiber - собственное расширение C для узла, своего рода многопоточное моделирование.
Это позволяет вам делать реальные sleep
способом, который блокирует выполнение в волокне, но не блокирует в основном потоке и других волокнах.
Вот пример, свежий из их собственного readme:
// sleep.js
var Fiber = require('fibers');
function sleep(ms) {
var fiber = Fiber.current;
setTimeout(function() {
fiber.run();
}, ms);
Fiber.yield();
}
Fiber(function() {
console.log('wait... ' + new Date);
sleep(1000);
console.log('ok... ' + new Date);
}).run();
console.log('back in main');
- и результаты:
$ node sleep.js
wait... Fri Jan 21 2011 22:42:04 GMT+0900 (JST)
back in main
ok... Fri Jan 21 2011 22:42:05 GMT+0900 (JST)
Или просто создайте это:
function yourFunction(){
//do something
setInterval(myFunc(),1000);
//do something else
}
function myFunc(){
return;
}
это просто будет ждать указанный интервал и вызовет функцию, которая ничего не будет делать.
Я искал / гуглил довольно много веб-страниц на javascript sleep / wait ... и НЕТ ответа, если вы хотите, чтобы javascript "RUN, DELAY, RUN" ... то, что получало большинство людей, было либо "RUN, RUN (бесполезные вещи), RUN "или" RUN, RUN + отложенный запуск "....
Итак, я съел несколько гамбургеров и подумал: вот решение, которое работает ... но вы должны нарезать свои текущие коды ... ::: да, я знаю, это легче читать рефакторинг ... еще ...
// ......................................... // пример1:
<html>
<body>
<div id="id1">DISPLAY</div>
<script>
//javascript sleep by "therealdealsince1982"; copyrighted 2009
//setInterval
var i = 0;
function run() {
//pieces of codes to run
if (i==0){document.getElementById("id1").innerHTML= "<p>code segment "+ i +" is ran</p>"; }
if (i==1){document.getElementById("id1").innerHTML= "<p>code segment "+ i +" is ran</p>"; }
if (i==2){document.getElementById("id1").innerHTML= "<p>code segment "+ i +" is ran</p>"; }
if (i >2){document.getElementById("id1").innerHTML= "<p>code segment "+ i +" is ran</p>"; }
if (i==5){document.getElementById("id1").innerHTML= "<p>all code segment finished running</p>"; clearInterval(t); } //end interval, stops run
i++; //segment of code finished running, next...
}
run();
t=setInterval("run()",1000);
</script>
</body>
</html>
// .................................... // example2 :
<html>
<body>
<div id="id1">DISPLAY</div>
<script>
//javascript sleep by "therealdealsince1982"; copyrighted 2009
//setTimeout
var i = 0;
function run() {
//pieces of codes to run, can use switch statement
if (i==0){document.getElementById("id1").innerHTML= "<p>code segment "+ i +" ran</p>"; sleep(1000);}
if (i==1){document.getElementById("id1").innerHTML= "<p>code segment "+ i +" ran</p>"; sleep(2000);}
if (i==2){document.getElementById("id1").innerHTML= "<p>code segment "+ i +" ran</p>"; sleep(3000);}
if (i==3){document.getElementById("id1").innerHTML= "<p>code segment "+ i +" ran</p>";} //stops automatically
i++;
}
function sleep(dur) {t=setTimeout("run()",dur);} //starts flow control again after dur
run(); //starts
</script>
</body>
</html>
// ................. пример3:
<html>
<body>
<div id="id1">DISPLAY</div>
<script>
//javascript sleep by "therealdealsince1982"; copyrighted 2009
//setTimeout
var i = 0;
function flow() {
run(i);
i++; //code segment finished running, increment i; can put elsewhere
sleep(1000);
if (i==5) {clearTimeout(t);} //stops flow, must be after sleep()
}
function run(segment) {
//pieces of codes to run, can use switch statement
if (segment==0){document.getElementById("id1").innerHTML= "<p>code segment "+ segment +" is ran</p>"; }
if (segment==1){document.getElementById("id1").innerHTML= "<p>code segment "+ segment +" is ran</p>"; }
if (segment==2){document.getElementById("id1").innerHTML= "<p>code segment "+ segment +" is ran</p>"; }
if (segment >2){document.getElementById("id1").innerHTML= "<p>code segment "+ segment +" is ran</p>"; }
}
function sleep(dur) {t=setTimeout("flow()",dur);} //starts flow control again after dur
flow(); //starts flow
</script>
</body>
</html>
// ........ ...... пример4:
<html>
<body>
<div id="id1">DISPLAY</div>
<script>
//javascript sleep by "therealdealsince1982"; copyrighted 2009
//setTimeout, switch
var i = 0;
function flow() {
switch(i)
{
case 0:
run(i);
sleep(1000);
break;
case 1:
run(i);
sleep(2000);
break;
case 5:
run(i);
clearTimeout(t); //stops flow
break;
default:
run(i);
sleep(3000);
break;
}
}
function run(segment) {
//pieces of codes to run, can use switch statement
if (segment==0){document.getElementById("id1").innerHTML= "<p>code segment "+ segment +" is ran</p>"; }
if (segment==1){document.getElementById("id1").innerHTML= "<p>code segment "+ segment +" is ran</p>"; }
if (segment==2){document.getElementById("id1").innerHTML= "<p>code segment "+ segment +" is ran</p>"; }
if (segment >2){document.getElementById("id1").innerHTML= "<p>code segment "+ segment +" is ran</p>"; }
i++; //current segment of code finished running, next...
}
function sleep(dur) {t=setTimeout("flow()",dur);} //starts flow control again after dur
flow(); //starts flow control for first time...
</script>
</body>
</html>
Примите асинхронную природу javascript!
Все последующее вернется немедленно, но у вас будет одно место для размещения кода, который вы хотите запустить после того, как что-то произошло.
Методы, которые я здесь описал, предназначены для разных случаев использования и примерно упорядочены по сложности.
Различаются следующие вещи:
Это довольно базовая реализация, предполагающая, что условие в какой-то момент станет истинным. С помощью нескольких настроек его можно расширить, чтобы сделать его еще более полезным (например, путем установки ограничения вызовов). (Я написал это только вчера!)
function waitFor(predicate, successCallback) {
setTimeout(function () {
var result = predicate();
if (result !== undefined)
successCallback(result);
else
waitFor(predicate, successCallback);
}, 100);
}
код вызова:
beforeEach(function (done) {
selectListField('A field');
waitFor(function () {
var availableOptions = stores.scrapeStore(optionStore);
if (availableOptions.length !== 0)
return availableOptions;
}, done);
});
Здесь я вызываю что-то, что загружает extjs 'store' и ждет, пока хранилище содержит что-то перед продолжением (beforeEach - это фреймворк для тестирования жасмина).
Дождаться завершения нескольких вещей
Еще одна вещь, которую мне нужно было сделать, - запустить один обратный вызов после завершения загрузки различных методов. Вы можете сделать это следующим образом:
createWaitRunner = function (completionCallback) {
var callback = completionCallback;
var completionRecord = [];
var elements = 0;
function maybeFinish() {
var done = completionRecord.every(function (element) {
return element === true
});
if (done)
callback();
}
return {
getNotifier: function (func) {
func = func || function (){};
var index = elements++;
completionRecord[index] = false;
return function () {
func.applyTo(arguments);
completionRecord[index] = true;
maybeFinish();
}
}
}
};
код вызова:
var waiter = createWaitRunner(done);
filterList.bindStore = waiter.getNotifier();
includeGrid.reconfigure = waiter.getNotifier(function (store) {
includeStore = store;
});
excludeGrid.reconfigure = waiter.getNotifier(function (store) {
excludeStore = store;
});
Вы либо просто ждете уведомлений, либо можете также обернуть другие функции, которые используют значения, переданные функции. Когда будут вызваны все методы, будет запущен done
.
Запуск асинхронных методов по порядку
Я использовал другой подход, когда у меня была серия асинхронных методов для вызова в строке (снова в тестах). Это в некоторой степени похоже на то, что вы можете получить в библиотеке Async - серия делает примерно то же самое, и я сначала немного прочитал эту библиотеку, чтобы посмотреть, получилось ли то, что я хотел. Я думаю, что у меня есть более приятный API для работы с тестами (+ это было интересно реализовать!).
//provides a context for running asyncronous methods syncronously
//the context just provides a way of sharing bits of state
//use run to execute the methods. These should be methods that take a callback and optionally the context as arguments
//note the callback is provided first so you have the option of just partially applying your function to the arguments you want
//instead of having to wrap even simple functions in another function
//when adding steps you can supply either just a function or a variable name and a function
//if you supply a variable name then the output of the function (which should be passed into the callback) will be written to the context
createSynchronisedRunner = function (doneFunction) {
var context = {};
var currentPosition = 0;
var steps = [];
//this is the loop. it is triggered again when each method finishes
var runNext = function () {
var step = steps[currentPosition];
step.func.call(null,
function (output) {
step.outputHandler(output);
currentPosition++;
if (currentPosition === steps.length)
return;
runNext();
}, context);
};
var api = {};
api.addStep = function (firstArg, secondArg) {
var assignOutput;
var func;
//overloads
if (secondArg === undefined) {
assignOutput = function () {
};
func = firstArg;
}
else {
var propertyName = firstArg;
assignOutput = function (output) {
context[propertyName] = output;
};
func = secondArg;
}
steps.push({
func: func,
outputHandler: assignOutput
});
};
api.run = function (completedAllCallback) {
completedAllCallback = completedAllCallback || function(){};
var lastStep = steps[steps.length - 1];
var currentHandler = lastStep.outputHandler;
lastStep.outputHandler = function (output) {
currentHandler(output);
completedAllCallback(context);
doneFunction();
};
runNext();
};
//this is to support more flexible use where you use a done function in a different scope to initialisation
//eg the done of a test but create in a beforeEach
api.setDoneCallback = function (done) {
doneFunction = done;
};
return api;
};
код вызова:
beforeAll(function (done) {
var runner = createSynchronisedRunner(done);
runner.addStep('attachmentInformation', testEventService.getAttachmentCalled.partiallyApplyTo('cat eating lots of memory.jpg'));
runner.addStep('attachment', getAttachment.partiallyApplyTo("cat eating lots of memory.jpg"));
runner.addStep('noAttachment', getAttachment.partiallyApplyTo("somethingElse.jpg"));
runner.run(function (context) {
attachment = context.attachment;
noAttachment = context.noAttachment;
});
});
PartiallyApplyTo здесь в основном переименованная версия реализации Curry Дуга Крокфорда. Многое из того, с чем я работаю, принимает в качестве последнего аргумента обратный вызов, поэтому простые вызовы можно выполнять, как это, вместо того, чтобы оборачивать все с помощью дополнительной функции.
Надеюсь, некоторые идеи там могут быть полезны для людей.