-
Notifications
You must be signed in to change notification settings - Fork 0
/
time-machined-loop.js
122 lines (112 loc) · 3.67 KB
/
time-machined-loop.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
'use strict';
// асинхронная итерация по произвольной undefined-терминированной питушне,
// выражающейся через getNextValue(cb),
// где cb принимает следующий элемент или undefined
// remember - сколько прошлых элементов помнить
// f - коллбек итерации цикла
// где f принимает x, next, done,
// где x - текущий элемент
// где next(n, cb) - функция для получения n-ого элемента
// где n - номер элемента, если 0 - номер текущего элемента,
// n<0 - прошлые, n>0 будущие
// где cb - функция, принимающая значение элемента или undefined, если нет
// cb - коллбек, вызывающийся после цикла
function asyncForEach (getNextValue, remember, f, cb) {
var nexts = [], prevs = [], maxPrevs = remember >= 0 ? remember + 1 : 1, ended = false;
var postponed = [], loading = false;
function getNextValueTailed (cb) {
if (ended) cb(undefined);
else getNextValue(function(x) {
if (x === undefined) ended = true;
cb(x);
});
}
function next(n, cb) {
if (n <= 0 && -n < prevs.length) cb(prevs[prevs.length+n-1]);
else if (n < 0) cb(undefined);
else if (n <= nexts.length) cb(nexts[n-1]);
else {
if (loading) {
postponed.push([n, cb]);
return;
}
loading = true;
(function load(i) {
getNextValueTailed(function(x) {
nexts.push(x);
if (i) load(i-1);
else{
loading = false;
cb(x);
if (postponed.length) {
var args = postponed.shift();
next(args[0], args[1]);
}
}
});
})(n - nexts.length - 1);
}
}
function process (x) {
if (x === undefined) {
cb();
return;
}
prevs.push(x);
if (prevs.length > maxPrevs) prevs.shift();
f(x, next, iteration);
}
function iteration() {
if (nexts.length) process(nexts.shift());
else getNextValue(process);
}
iteration();
}
// генерация случайных последовательностей
function randoms (n) {
return function (cb) {
if (n < 0) throw new Error('Sosnooley!');
setTimeout(function() {
cb(n-- ? Math.random() * 1000 | 0 : undefined);
}, Math.random() * 1000 | 0);
}
}
// демонстрация циклоняшества
var i = 0;
console.log('Start loop #1.');
asyncForEach(randoms(10), 2, function(x, next, done) {
i ++;
next(-4, function(prev4) {
next(-3, function(prev3) {
next(-2, function(prev2) {
next(-1, function(prev1) {
next(0, function(cur) {
next(1, function(next1) {
next(2, function(next2) {
next(3, function(next3) {
console.log('Loop #1, iteration #' + i + ': x=' + x + '=' + cur +
' prevs=' + JSON.stringify([prev4, prev3, prev2, prev1]) +
' nexts=' + JSON.stringify([next1, next2, next3]));
done();
});
});
});
});
});
});
});
});
}, function(){
console.log('End loop #1.');
var i = 0;
console.log('Start loop #2');
asyncForEach(randoms(10), 1, function(x, next, done) {
i ++;
next(-1, function(prev) {
console.log('Loop #2, iteration #' + i + ': x=' + x + ' prev=' + prev);
done();
});
}, function(){
console.log('End loop #2.');
});
});