Я перебираю решение в течение дня, но все еще думаю, как сохранить цепочку при использовании обратного вызова. Все знакомы с традиционным стилем программирования, в котором код выполняется построчно, синхронно. 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, это может вызвать проблемы, если он работает в строгом режиме.
64-разрядная Windows прекрасно работает с 32-разрядной Visual Studio. Если вы не укажете, что хотите использовать инструменты разработки x64, он по-прежнему будет компилировать 32-разрядные приложения.
Прямо со страницы:
Visual Studio использует 32-разрядный кросс компилятор даже на 64-битной Windows компьютер. Однако вы можете использовать devenv команды для создания командной строки среда для вызова 64-битной размещенной инструменты.
Дополнительная информация: http://msdn.microsoft.com/en-us/library/ms246588 (VS.80) .aspx
С Visual Studio вы можете выбрать платформу. По умолчанию он будет работать на «любом процессоре» (32- или 64-разрядный), но вы можете указать, если хотите. В разделе «Проект»> «Свойства»> «Сборка» найдите свойство «Целевая платформа».
Да. 64-битная перспектива будет запускать 32-битные исполняемые файлы, поэтому, если у вас есть 32-битный компилятор, он все равно будет работать.
В Visual Studio вы можете указать, что нужно компилировать в диспетчере конфигураций - (Меню сборки - Диспетчер конфигурации) - это позволяет выбрать 32- или 64-разрядную версию.
64-битное потребительское оборудование обычно представляет собой архитектуру amd64, которая может запускать как 32-битные приложения, так и 64-битные приложения. 64-разрядная версия Windows Vista поддерживает как 32-разрядные, так и 64-разрядные системные библиотеки, поэтому в основном вы можете запускать приложения обоих типов. (Обратите внимание, что архитектура IA-64 не допускает этого.)
Компиляция программы в 64-битной среде мало связана с платформой, на которой работает компилятор. Но, конечно же, для запуска и тестирования результирующего двоичного файла требуется соответствующая архитектура.
Как многие упоминали выше, VS2008 позволяет вам выбирать целевую архитектуру, поэтому нет никаких проблем.