Skip to content

Commit de269d0

Browse files
formatting done
1 parent 7af5b3c commit de269d0

1 file changed

Lines changed: 1 addition & 91 deletions

File tree

  • 1-js/12-generators-iterators/1-generators

1-js/12-generators-iterators/1-generators/article.md

Lines changed: 1 addition & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -211,43 +211,33 @@ let range = {
211211
alert([...range]); // 1,2,3,4,5
212212
```
213213

214-
215214
دلیل کارکرد روش بالا این است که `()range[Symbol.iterator]` دقیقا همان چیزی را برمی‌گرداند که `for..of` انتظار دارد:
216-
217215
- متود `next` موجود است.
218-
219216
- مقدار بازگشتی به فرم `{value:..., done:true/false}` است.
220217

221218
این کارکرد یک اتفاق نیست، generatorها با توجه به iterator ها، برای پیاده‌سازی ساده‌تر آن‌ها به زبان اضافه شده‌اند.
222219

223220
روشی که از generator استفاده می‌کند بسیار مختصرتر از روش اول `range` است و همان کارکرد را دارد.
224221

225222
```smart header="generatorها ممکن است تا ابد مقدار تولید کنند"
226-
227223
در مثال بالا، یک دنباله کران‌دار تولید کردیم، ولی می‌توان به همان روش یک دنباله بی‌کران از مقادیر را ساخت. مثل یک دنباله بی‌پایان از اعداد شبه تصادفی.
228224
225+
چنین کاربردی قطعا به یک `break` یا `return` در `for..of` نیاز دارد. در غیر این صورت حلقه تا ابد تکرار می‌شود.
229226
```
230227

231-
232228
## ترکیب generatorها
233229

234230
ترکیب generatorها قابلیتی است که به وسیله آن می‌توان generatorها را در هم `embed` کرد.
235231

236232
برای مثال یک generator داریم که یک دنباله از اعداد را تولید می‌کند:
237233

238234
```js
239-
240235
function* generateSequence(start, end) {
241-
242236
for (let i = start; i <= end; i++) yield i;
243-
244237
}
245-
246238
```
247239

248-
249240
اکنون می‎‌خواهیم از آن به نحوی بازاستفاده کنیم که دنباله پیچیده‌تری بتوان ایجاد کرد:
250-
251241
- ابتدا ارقام از `0` تا `9`(با کد کاراکتر 48 تا 57)
252242
- سپس حروف بزرگ از `A` تا `Z`(با کد کاراکتر 65 تا 90)
253243
- سپس حروف کوچک از `a` تا `z`(با کد کاراکتر 97 تا 122)
@@ -261,91 +251,67 @@ function* generateSequence(start, end) {
261251
generator ترکیب شده:
262252

263253
```js run
264-
265254
function* generateSequence(start, end) {
266-
267255
for (let i = start; i <= end; i++) yield i;
268-
269256
}
270257

271258
function* generatePasswordCodes() {
272259

273260
\*!*
274-
275261
// 0..9
276-
277262
yield* generateSequence(48, 57);
278263

279264
// A..Z
280-
281265
yield* generateSequence(65, 90);
282266

283267
// a..z
284-
285268
yield* generateSequence(97, 122);
286-
287269
\*/!*
288270

289271
}
290272

291273
let str = '';
292274

293275
for(let code of generatePasswordCodes()) {
294-
295276
str += String.fromCharCode(code);
296-
297277
}
298278

299279
alert(str); // 0..9A..Za..z
300-
301280
```
302281

303-
304282
عبارت `*yield` اجرا را به یک generator دیگر می‌سپارد(اصطلاحا `delegate` می‌کند). بدان معنا که `yeild* gen` روی `gen` ایتریت می‌کند و به صورت درونی مقدار yield شده را به بیرون هدایت می‌کند؛ انگار که کلا مقدار توسط generator دوم تولید شده است.
305283

306284
اگر از generatorها به صورت inline و تو در تو استفاده کنیم نیز به همان نتیجه می‌رسیم:
307285

308286
```js run
309-
310287
function* generateSequence(start, end) {
311-
312288
for (let i = start; i <= end; i++) yield i;
313-
314289
}
315290

316291
function* generateAlphaNum() {
317292

318293
\*!*
319-
320294
// yield* generateSequence(48, 57);
321-
322295
for (let i = 48; i <= 57; i++) yield i;
323296

324297
// yield* generateSequence(65, 90);
325-
326298
for (let i = 65; i <= 90; i++) yield i;
327299

328300
// yield* generateSequence(97, 122);
329-
330301
for (let i = 97; i <= 122; i++) yield i;
331-
332302
\*/!*
333303

334304
}
335305

336306
let str = '';
337307

338308
for(let code of generateAlphaNum()) {
339-
340309
str += String.fromCharCode(code);
341-
342310
}
343311

344312
alert(str); // 0..9A..Za..z
345-
346313
```
347314

348-
349315
ترکیب generatorها یک راه معقول برای استفاده از جریان یک generator درون دیگری است و از حافظه بیشتر برای ذخیره مقادیر استفاده نمی‌کند.
350316

351317
## yield یک خیابان دو طرفه است.
@@ -359,67 +325,50 @@ alert(str); // 0..9A..Za..z
359325
برای مثال:
360326

361327
```js run
362-
363328
function* gen() {
364-
365329
\*!*
366-
367330
// یک سوال را به کد بیرونی برگردانید و منتظر جواب شوید
368-
369331
let result = yield "2 + 2 = ?"; // (*)
370-
371332
\*/!*
372333

373334
alert(result);
374-
375335
}
376336

377337
let generator = gen();
378338

379339
let question = generator.next().value; // <-- مقدار را بر می‌گرداند yield
380340

381341
generator.next(4); // --> برمی‌گرداند generator نتیجه را به
382-
383342
```
384343

385-
386344
![](genYield2.svg)
387345

388346
1. نمی‌توان اولین بار که `()generator.next` صدا می‌شود به آن argument داد و در صورت داده شدن، نادید گرفته خواهد شد. پس از صدا شدن متود، اجرای generator شروع می‌شود و مقدار اولین yield را برمی‌گرداند. اکنون اجرای generator متوقف شده و در خط `*` مانده است.
389-
390347
2. سپس مانند تصویر بالا، نتیجه yield اول در متغیر `question` ذخیره می‌شود.
391-
392348
3. با اجرای `generator.next(4)`، اجرای generator دوباره شروع می‌‎شود و مقدار متغیر `result` برابر `4` می‌شود.
393349

394350
توجه داشته باشید که نیاز نیست کد بیرونی فورا `(4)next` را صدا کند؛ اگر طول بکشد، generator صبر خواهد کرد.
395351

396352
برای مثال:
397353

398354
```js
399-
400355
// پس از تاخیری دوباره شروع می‌شود generator اجرای
401-
402356
setTimeout(() => generator.next(4), 1000);
403-
404357
```
405358

406-
407359
همان‌طور که مشاهده می‌شود بر خلاف توابع معمولی، یک generator و کد صدا زننده‌اش می‌توانند با هم مقادیر را از طریق `next/yield` رد و بدل کنند.
408360

409361
یک مثال دیگر:
410362

411363
```js run
412-
413364
function* gen() {
414-
415365
let ask1 = yield "2 + 2 = ?";
416366

417367
alert(ask1); // 4
418368

419369
let ask2 = yield "3 * 3 = ?";
420370

421371
alert(ask2); // 9
422-
423372
}
424373

425374
let generator = gen();
@@ -429,22 +378,16 @@ alert(generator.next().value); // "2 + 2 = ?"
429378
alert(generator.next(4).value); // "3 * 3 = ?"
430379

431380
alert(generator.next(9).done); // true
432-
433381
```
434382

435-
436383
تصویر اجرا:
437384

438385
![](genYield2-2.svg)
439386

440387
1. اولین `()next`اجرای generator را آغاز می‌کند تا به اولین yield برسد.
441-
442388
2. نتیجه به کد بیرونی برگردانده می‌شود.
443-
444389
3. صدا زده شدن `next(4)` مقدار `4` را به generator به عنوان نتیجه اولین yield باز می‌گرداند و اجرای generator را دوباره شروع می‌کند.
445-
446390
4. به yield دوم می‌رسد و مقدار آن نتیجه دومین بار صدا شدن `next` است.
447-
448391
5. صدا زده شدن `next(9)`مقدار `9` را به عنوان نتیجه دومین yield برمی‌گرداند و اجرای generator دوباره شروع می‌شود تا به انتهای تابع، `done:true` برسد.
449392

450393
درست مثل بازی پینگ پنگ؛ `(value)next` یک مقدار را به generator پاس می‌دهد که نتیجه yield فعلی می‌شود و سپس نتیجه yield بعدی به بیرون پاس داده می‌شود.
@@ -460,110 +403,77 @@ alert(generator.next(9).done); // true
460403
برای مثال اینجا yield شدن "2 + 2 = ?" باعث ارور می‌شود:
461404

462405
```js run
463-
464406
function* gen() {
465-
466407
try {
467-
468408
let result = yield "2 + 2 = ?"; // (1)
469409

470410
alert("اجرا به اینجا نمی‌رسد چون خط بالا ارور پرتاب کرده است");
471-
472411
} catch(e) {
473-
474412
alert(e); // ارور را نشان می‌دهد
475-
476413
}
477-
478414
}
479415

480416
let generator = gen();
481417

482418
let question = generator.next().value;
483419

484420
\*!*
485-
486421
generator.throw(new Error("پاسخ در دیتابیس من نیست")); // (2)
487-
488422
\*/!*
489-
490423
```
491424

492-
493425
ارور پرتاب شده به داخل generator در خط `2` باعث exception در خط `1` دارای yield می‌شود که در مثال بالا توسط `try..catch` گرفته شده و نمایش داده می‌شود.
494426

495427
اگر آن را catch نکنیم، مانند هر exception دیگری اجرا از generator به کد بیرونی منتقل می‌شود.
496428

497429
خط فعلی کد صدا زننده، خط دارای `generator.throw` با لیبل `2` است. پس خطا را این گونه هم می‌توان گرفت:
498430

499431
```js run
500-
501432
function* generate() {
502-
503433
let result = yield "2 + 2 = ?"; // خطا در این خط
504-
505434
}
506435

507436
let generator = generate();
508437

509438
let question = generator.next().value;
510439

511440
\*!*
512-
513441
try {
514-
515442
generator.throw(new Error("پاسخ در دیتابیس من نیست"));
516-
517443
} catch(e) {
518-
519444
alert(e); // ارور را نمایش می‌دهد
520-
521445
}
522-
523446
\*/!*
524-
525447
```
526448

527-
528449
اگر ارور را catch نکنیم، در صورت وجود کد بیرونی اجرا به آن منتقل می‌شود و اگر آن‌جا نیز هندل نشده باشد، اجرای کد با خطا پایان می‌پذیرد.
529450

530451
## generator.return
531452

532453
این متود اجرای generator را به اتمام می‌رساند و مقدار argument را به عنوان نتیجه برمی‌گرداند.
533454

534455
```js
535-
536456
function* gen() {
537-
538457
yield 1;
539-
540458
yield 2;
541-
542459
yield 3;
543-
544460
}
545461

546462
const g = gen();
547463

548464
g.next(); // { value: 1, done: false }
549-
550465
g.return("foo"); // { value: "foo", done: true }
551-
552466
g.next(); // { value: undefined, done: true }
553-
554467
```
555468

556-
557469
اگر در یک generatorخاتمه یافته دوباره از `()generator.return`استفاده کنیم، همان مقدار را دوباره برمی‌گرداند.([MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Generator/return)).
558470

559471
معمولا از آن استفاده نمی‌شود؛ چون اکثر زمان‌ها می‌خواهیم تمام مقادیر را بگیریم، اما موقعی که بخواهیم در شرایط خاص generator را متوقف کنیم کاربرد دارد.
560472

561473
## خلاصه
562474

563475
- generatorها توسط تابع generator تولید می‌شوند. `{...} (...) function* f`
564-
565476
- عملگر yield (فقط) در داخل generatorها وجود دارد.
566-
567477
- کد بیرونی و generator ممکن است توسط `next/yield` با هم نتایج را رد و بدل کنند.
568478

569479
در جاوااسکریپت مدرن، generatorها کم استفاده می‌شوند. اما گاهی اوقات می‌توانند مفید باشند؛ رد و بدل کردن داده با کد صدا زننده، یک قابلیت منحصر بفرد است. هم‌چنین برای ساخت iterableها هم بکار می‌روند.

0 commit comments

Comments
 (0)