##// END OF EJS Templates
Fixed circular dependency safe<->Cancellation
cin -
r150:b16ed1178612 v1.4.0-rc7 default
parent child
Show More
@@ -1,502 +1,518
1 import { ICancellable, Constructor, IDestroyable, PromiseOrValue } from "./interfaces";
2 import { Cancellation } from "./Cancellation";
1 import { ICancellable, Constructor, IDestroyable, ICancellation } from "./interfaces";
3 2
4 3 let _nextOid = 0;
5 4 const _oid = typeof Symbol === "function" ?
6 5 Symbol("__implab__oid__") :
7 6 "__implab__oid__";
8 7
9 8 export function oid(instance: null | undefined): undefined;
10 9 export function oid(instance: NonNullable<any>): string;
11 10 export function oid(instance: any): string | undefined {
12 11 if (isNull(instance))
13 12 return undefined;
14 13
15 14 if (_oid in instance)
16 15 return instance[_oid];
17 16 else
18 17 return (instance[_oid] = "oid_" + (++_nextOid));
19 18 }
20 19
20 const cancellationNone: ICancellation = {
21 isSupported(): boolean {
22 return false;
23 },
24
25 throwIfRequested(): void {
26 },
27
28 isRequested(): boolean {
29 return false;
30 },
31
32 register(_cb: (e: any) => void): IDestroyable {
33 return destroyed;
34 }
35 };
36
21 37 export function keys<T>(arg: T): (Extract<keyof T, string>)[] {
22 38 return isObject(arg) && arg ? Object.keys(arg) as (Extract<keyof T, string>)[] : [];
23 39 }
24 40
25 41 export function isKeyof<T>(k: string, target: T): k is Extract<keyof T, string> {
26 42 return target && typeof target === "object" && k in target;
27 43 }
28 44
29 45 export function argumentNotNull(arg: any, name: string) {
30 46 if (arg === null || arg === undefined)
31 47 throw new Error("The argument " + name + " can't be null or undefined");
32 48 }
33 49
34 50 export function argumentNotEmptyString(arg: any, name: string) {
35 51 if (typeof (arg) !== "string" || !arg.length)
36 52 throw new Error("The argument '" + name + "' must be a not empty string");
37 53 }
38 54
39 55 export function argumentNotEmptyArray(arg: any, name: string) {
40 56 if (!(arg instanceof Array) || !arg.length)
41 57 throw new Error("The argument '" + name + "' must be a not empty array");
42 58 }
43 59
44 60 export function argumentOfType(arg: any, type: Constructor<{}>, name: string) {
45 61 if (!(arg instanceof type))
46 62 throw new Error("The argument '" + name + "' type doesn't match");
47 63 }
48 64
49 65 export function isObject(val: any): val is object {
50 66 return typeof val === "object";
51 67 }
52 68
53 69 export function isNull(val: any): val is null | undefined {
54 70 return (val === null || val === undefined);
55 71 }
56 72
57 73 export type primitive = symbol | string | number | boolean | undefined | null;
58 74
59 75 export function isPrimitive(val: any): val is primitive {
60 76 return (val === null || val === undefined || typeof (val) === "string" ||
61 77 typeof (val) === "number" || typeof (val) === "boolean");
62 78 }
63 79
64 80 export function isInteger(val: any): val is number {
65 81 return parseInt(val, 10) === val;
66 82 }
67 83
68 84 export function isNumber(val: any): val is number {
69 85 return parseFloat(val) === val;
70 86 }
71 87
72 88 export function isString(val: any): val is string {
73 89 return typeof (val) === "string" || val instanceof String;
74 90 }
75 91
76 92 export function isPromise<T = any>(val: any): val is PromiseLike<T> {
77 93 return val && typeof val.then === "function";
78 94 }
79 95
80 96 export function isCancellable(val: any): val is ICancellable {
81 97 return val && typeof val.cancel === "function";
82 98 }
83 99
84 100 export function isNullOrEmptyString(val: any): val is ("" | null | undefined) {
85 101 return (val === null || val === undefined ||
86 102 ((typeof (val) === "string" || val instanceof String) && val.length === 0));
87 103 }
88 104
89 105 export function isNotEmptyArray<T = any>(arg: any): arg is T[] {
90 106 return (arg instanceof Array && arg.length > 0);
91 107 }
92 108
93 109 function _isStrictMode(this: any) {
94 110 return !this;
95 111 }
96 112
97 113 function _getNonStrictGlobal(this: any) {
98 114 return this;
99 115 }
100 116
101 117 export function getGlobal() {
102 118 // in es3 we can't use indirect call to eval, since it will
103 119 // be executed in the current call context.
104 120 if (!_isStrictMode()) {
105 121 return _getNonStrictGlobal();
106 122 } else {
107 123 // tslint:disable-next-line:no-eval
108 124 return eval.call(null, "this");
109 125 }
110 126 }
111 127
112 128 export function get(member: string, context?: object) {
113 129 argumentNotEmptyString(member, "member");
114 130 let that = context || getGlobal();
115 131 const parts = member.split(".");
116 132 for (const m of parts) {
117 133 if (!m)
118 134 continue;
119 135 if (isNull(that = that[m]))
120 136 break;
121 137 }
122 138 return that;
123 139 }
124 140
125 141 /**
126 142 * ВыполняСт ΠΌΠ΅Ρ‚ΠΎΠ΄ для ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ элСмСнта массива, останавливаСтся, ΠΊΠΎΠ³Π΄Π°
127 143 * Π»ΠΈΠ±ΠΎ достигнут ΠΊΠΎΠ½Π΅Ρ† массива, Π»ΠΈΠ±ΠΎ функция <c>cb</c> Π²Π΅Ρ€Π½ΡƒΠ»Π°
128 144 * Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅.
129 145 *
130 146 * @param {Array | Object} obj массив элСмСнтов для просмотра
131 147 * @param {Function} cb функция, вызываСмая для ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ элСмСнта
132 148 * @param {Object} thisArg Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅, ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠ΅ Π±ΡƒΠ΄Π΅Ρ‚ ΠΏΠ΅Ρ€Π΅Π΄Π°Π½ΠΎ Π² качСствС
133 149 * <c>this</c> Π² <c>cb</c>.
134 150 * @returns {void}
135 151 */
136 152 export function each<T>(obj: T, cb: <X extends keyof T>(v: NonNullable<T[X]>, k: X) => void): void;
137 153 export function each<T>(array: T[], cb: (v: T, i: number) => void): void;
138 154 export function each(obj: any, cb: any, thisArg?: any): any;
139 155 export function each(obj: any, cb: any, thisArg?: any) {
140 156 argumentNotNull(cb, "cb");
141 157 if (obj instanceof Array) {
142 158 let v: any;
143 159 for (let i = 0; i < obj.length; i++) {
144 160 v = obj[i];
145 161 if (v !== undefined)
146 162 cb.call(thisArg, v, i);
147 163 }
148 164 } else {
149 165 Object.keys(obj).forEach(k => obj[k] !== undefined && cb.call(thisArg, obj[k], k));
150 166 }
151 167 }
152 168
153 169 /** Copies property values from a source object to the destination and returns
154 170 * the destination object.
155 171 *
156 172 * @param dest The destination object into which properties from the source
157 173 * object will be copied.
158 174 * @param source The source of values which will be copied to the destination
159 175 * object.
160 176 * @param template An optional parameter specifies which properties should be
161 177 * copied from the source and how to map them to the destination. If the
162 178 * template is an array it contains the list of property names to copy from the
163 179 * source to the destination. In case of object the templates contains the map
164 180 * where keys are property names in the source and the values are property
165 181 * names in the destination object. If the template isn't specified then the
166 182 * own properties of the source are entirely copied to the destination.
167 183 *
168 184 */
169 185 export function mixin<T extends object, S extends object>(dest: T, source: S, template?: (keyof S)[]): T & S;
170 186 export function mixin<T extends object, S extends object, R extends object = T>(dest: T, source: S, template: { [p in keyof S]?: keyof R; }): T & R;
171 187 export function mixin<T extends object, S extends object>(dest: T, source: S, template?: any): any {
172 188 argumentNotNull(dest, "dest");
173 189 const _res: any = dest as any;
174 190
175 191 if (isPrimitive(source))
176 192 return _res;
177 193
178 194 if (template instanceof Array) {
179 195 template.forEach(p => {
180 196 if (isKeyof(p, source))
181 197 _res[p] = source[p];
182 198 });
183 199 } else if (template) {
184 200 keys(source).forEach(p => {
185 201 if (isKeyof(p, template))
186 202 _res[template[p]] = source[p];
187 203 });
188 204 } else {
189 205 keys(source).forEach(p => _res[p] = source[p]);
190 206 }
191 207
192 208 return _res;
193 209 }
194 210
195 211 /** Wraps the specified function to emulate an asynchronous execution.
196 212 * @param{Object} thisArg [Optional] Object which will be passed as 'this' to the function.
197 213 * @param{Function|String} fn [Required] Function wich will be wrapped.
198 214 */
199 215 export function async<T, F extends (...args: any[]) => T | PromiseLike<T>>(
200 216 fn: F,
201 217 thisArg?: ThisParameterType<F>
202 218 ): (...args: Parameters<F>) => PromiseLike<T>;
203 219 export function async<T, M extends string, O extends { [m in M]?: (...args: any[]) => T | PromiseLike<T> }>(
204 220 fn: M,
205 221 thisArg: O
206 222 ): (...args: Parameters<NonNullable<O[M]>>) => PromiseLike<T>;
207 223 export function async(_fn: any, thisArg: any): (...args: any[]) => PromiseLike<any> {
208 224 let fn = _fn;
209 225
210 226 if (arguments.length === 2 && !(fn instanceof Function))
211 227 fn = thisArg[fn];
212 228
213 229 if (fn == null)
214 230 throw new Error("The function must be specified");
215 231
216 232 function wrapresult(x: any, e?: any): PromiseLike<any> {
217 233 if (e) {
218 234 return {
219 235 then(cb, eb) {
220 236 try {
221 237 return eb ? wrapresult(eb(e)) : this;
222 238 } catch (e2) {
223 239 return wrapresult(null, e2);
224 240 }
225 241 }
226 242 };
227 243 } else {
228 244 if (x && x.then)
229 245 return x;
230 246 return {
231 247 then(cb) {
232 248 try {
233 249 return cb ? wrapresult(cb(x)) : this;
234 250 } catch (e2) {
235 251 return wrapresult(e2);
236 252 }
237 253 }
238 254 };
239 255 }
240 256 }
241 257
242 258 return (...args) => {
243 259 try {
244 260 return wrapresult(fn.apply(thisArg, args));
245 261 } catch (e) {
246 262 return wrapresult(null, e);
247 263 }
248 264 };
249 265 }
250 266
251 267 export function delegate<T extends object, F extends (this: T, ...args: any[]) => any>(
252 268 target: T,
253 269 method: F
254 270 ): OmitThisParameter<F>;
255 271 export function delegate<M extends string, T extends { [m in M]?: (...args: any[]) => any; }>(
256 272 target: T,
257 273 method: M
258 274 ): OmitThisParameter<T[M]>;
259 275 export function delegate(target: any, _method: any): (...args: any[]) => any {
260 276 let method: any;
261 277 if (!(_method instanceof Function)) {
262 278 argumentNotNull(target, "target");
263 279 method = target[_method];
264 280 if (!(method instanceof Function))
265 281 throw new Error("'method' argument must be a Function or a method name");
266 282 } else {
267 283 method = _method;
268 284 }
269 285
270 286 return (...args) => {
271 287 return method.apply(target, args);
272 288 };
273 289 }
274 290
275 export function delay(timeMs: number, ct = Cancellation.none) {
291 export function delay(timeMs: number, ct = cancellationNone) {
276 292 ct.throwIfRequested();
277 293 return new Promise((resolve, reject) => {
278 294 const h = ct.register(e => {
279 295 clearTimeout(id);
280 296 reject(e);
281 297 // we don't nedd to unregister h, since ct is already disposed
282 298 });
283 299 const id = setTimeout(() => {
284 300 h.destroy();
285 301 resolve();
286 302 }, timeMs);
287 303
288 304 });
289 305 }
290 306
291 307 /** Returns resolved promise, awaiting this method will cause the asynchronous
292 308 * completion of the rest of the code.
293 309 */
294 310 export function fork() {
295 311 return Promise.resolve();
296 312 }
297 313
298 314 /** Always throws Error, can be used as a stub for the methods which should be
299 315 * assigned later and are required to be not null.
300 316 */
301 317 export function notImplemented(): never {
302 318 throw new Error("Not implemeted");
303 319 }
304 320 /**
305 321 * Iterates over the specified array of items and calls the callback `cb`, if
306 322 * the result of the callback is a promise the next item from the array will be
307 323 * proceeded after the promise is resolved.
308 324 *
309 325 */
310 326 export function pmap<T, T2>(
311 327 items: ArrayLike<T> | PromiseLike<ArrayLike<T>>,
312 328 cb: (item: T, i: number) => T2 | PromiseLike<T2>
313 329 ): T2[] | PromiseLike<T2[]> {
314 330 argumentNotNull(cb, "cb");
315 331
316 332 if (isPromise(items)) {
317 333 return items.then(data => pmap(data, cb));
318 334 } else {
319 335
320 336 if (isNull(items) || !items.length)
321 337 return [];
322 338
323 339 let i = 0;
324 340 const result = new Array<T2>();
325 341
326 342 const next = (): any => {
327 343 while (i < items.length) {
328 344 const r = cb(items[i], i);
329 345 const ri = i;
330 346 i++;
331 347 if (isPromise(r)) {
332 348 return r.then(x => {
333 349 result[ri] = x;
334 350 return next();
335 351 });
336 352 } else {
337 353 result[ri] = r;
338 354 }
339 355 }
340 356 return result;
341 357 };
342 358
343 359 return next();
344 360 }
345 361 }
346 362
347 363 export function pfor<T>(
348 364 items: ArrayLike<T> | PromiseLike<ArrayLike<T>>,
349 365 cb: (item: T, i: number) => any
350 366 ): void | PromiseLike<void> {
351 367 argumentNotNull(cb, "cb");
352 368
353 369 if (isPromise(items)) {
354 370 return items.then(data => pfor(data, cb));
355 371 } else {
356 372 if (isNull(items) || !items.length)
357 373 return;
358 374
359 375 let i = 0;
360 376
361 377 const next = (): any => {
362 378 while (i < items.length) {
363 379 const r = cb(items[i], i);
364 380 i++;
365 381 if (isPromise(r))
366 382 return r.then(next);
367 383 }
368 384 };
369 385
370 386 return next();
371 387 }
372 388 }
373 389
374 390 export function first<T>(sequence: ArrayLike<T>): T;
375 391 export function first<T>(sequence: PromiseLike<ArrayLike<T>>): PromiseLike<T>;
376 392 export function first<T>(
377 393 sequence: ArrayLike<T> | PromiseLike<ArrayLike<T>>,
378 394 cb?: (x: T) => void,
379 395 err?: (x: Error) => void
380 396 ): void;
381 397 /**
382 398 * Π’Ρ‹Π±ΠΈΡ€Π°Π΅Ρ‚ ΠΏΠ΅Ρ€Π²Ρ‹ΠΉ элСмСнт ΠΈΠ· ΠΏΠΎΡΠ»Π΅Π΄ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΠ½ΠΎΡΡ‚ΠΈ, ΠΈΠ»ΠΈ обСщания, Ссли Π²
383 399 * качСствС ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Π° ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ ΠΎΠ±Π΅Ρ‰Π°Π½ΠΈΠ΅, ΠΎΠ½ΠΎ Π΄ΠΎΠ»ΠΆΠ½ΠΎ Π²Π΅Ρ€Π½ΡƒΡ‚ΡŒ массив.
384 400 *
385 401 * @param {Function} cb ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊ Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚Π°, Π΅ΠΌΡƒ Π±ΡƒΠ΄Π΅Ρ‚ ΠΏΠ΅Ρ€Π΅Π΄Π°Π½ ΠΏΠ΅Ρ€Π²Ρ‹ΠΉ
386 402 * элСмСнт ΠΏΠΎΡΠ»Π΅Π΄ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΠ½ΠΎΡΡ‚ΠΈ Π² случаС успСха
387 403 * @param {Function} err ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊ ΠΈΡΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΡ, Ссли массив пустой, Π»ΠΈΠ±ΠΎ
388 404 * нС массив
389 405 *
390 406 * @remarks Если Π½Π΅ ΡƒΠΊΠ°Π·Π°Π½Ρ‹ Π½ΠΈ cb Π½ΠΈ err, Ρ‚ΠΎΠ³Π΄Π° функция Π²Π΅Ρ€Π½Π΅Ρ‚ Π»ΠΈΠ±ΠΎ
391 407 * ΠΎΠ±Π΅Ρ‰Π°Π½ΠΈΠ΅, Π»ΠΈΠ±ΠΎ ΠΏΠ΅Ρ€Π²Ρ‹ΠΉ элСмСнт.
392 408 * @async
393 409 */
394 410 export function first<T>(
395 411 sequence: ArrayLike<T> | PromiseLike<ArrayLike<T>>,
396 412 cb?: (x: T) => void,
397 413 err?: (x: Error) => void
398 414 ) {
399 415 if (isPromise(sequence)) {
400 416 return sequence.then(res => first(res, cb as any /* force to pass undefined cb */, err));
401 417 } else if (sequence && "length" in sequence) {
402 418 if (sequence.length === 0) {
403 419 if (err)
404 420 return err(new Error("The sequence is empty"));
405 421 else
406 422 throw new Error("The sequence is empty");
407 423 } else if (cb) {
408 424 return cb(sequence[0]);
409 425 } else {
410 426 return sequence[0];
411 427 }
412 428 } else {
413 429 if (err)
414 430 return err(new Error("The sequence is required"));
415 431 else
416 432 throw new Error("The sequence is required");
417 433 }
418 434 }
419 435
420 436 export function firstWhere<T>(
421 437 sequence: ArrayLike<T>,
422 438 predicate: (x: T) => boolean
423 439 ): T;
424 440 export function firstWhere<T>(
425 441 sequence: PromiseLike<ArrayLike<T>>,
426 442 predicate: (x: T) => boolean
427 443 ): PromiseLike<T>;
428 444 export function firstWhere<T>(
429 445 sequence: ArrayLike<T> | PromiseLike<ArrayLike<T>>,
430 446 predicate: (x: T) => boolean,
431 447 cb: (x: T) => void,
432 448 err?: (x: Error) => void
433 449 ): void;
434 450
435 451 export function firstWhere<T>(
436 452 sequence: ArrayLike<T> | PromiseLike<ArrayLike<T>>,
437 453 predicate?: (x: T) => boolean,
438 454 cb?: (x: T) => any,
439 455 err?: (x: Error) => any
440 456 ) {
441 457 if (isPromise(sequence)) {
442 458 return sequence.then(res => firstWhere(
443 459 res,
444 460 predicate as any /* force to pass undefined predicate */,
445 461 cb as any /* force to pass undefined cb */,
446 462 err)
447 463 );
448 464 } else if (sequence && "length" in sequence) {
449 465 if (sequence.length === 0) {
450 466 if (err)
451 467 err(new Error("The sequence is empty"));
452 468 else
453 469 throw new Error("The sequence is empty");
454 470 } else {
455 471 if (!predicate) {
456 472 return cb ? cb(sequence[0]) && void (0) : sequence[0];
457 473 } else {
458 474 for (let i = 0; i < sequence.length; i++) {
459 475 const v = sequence[i];
460 476 if (predicate(v))
461 477 return cb ? cb(v) : v;
462 478 }
463 479 if (err)
464 480 err(new Error("The sequence doesn't contain matching items"));
465 481 else
466 482 throw new Error("The sequence doesn't contain matching items");
467 483 }
468 484 }
469 485 } else {
470 486 if (err)
471 487 err(new Error("The sequence is required"));
472 488 else
473 489 throw new Error("The sequence is required");
474 490 }
475 491 }
476 492
477 493 export function isDestroyable(d: any): d is IDestroyable {
478 494 if (d && "destroy" in d && typeof (destroy) === "function")
479 495 return true;
480 496 return false;
481 497 }
482 498
483 499 export function destroy(d: any) {
484 500 if (d && "destroy" in d)
485 501 d.destroy();
486 502 }
487 503
488 504 /**
489 505 * Used to mark that the async operation isn't awaited intentionally.
490 506 * @param p The promise which represents the async operation.
491 507 */
492 508 export function nowait(p: Promise<any>) {
493 509 }
494 510
495 511 /** represents already destroyed object.
496 512 */
497 513 export const destroyed = {
498 514 /** Calling to this method doesn't affect anything, noop.
499 515 */
500 516 destroy() {
501 517 }
502 518 };
General Comments 0
You need to be logged in to leave comments. Login now