تکرار ناهمگام در JavaScript
ﺯﻣﺎﻥ ﻣﻄﺎﻟﻌﻪ: 3 دقیقه

تکرار ناهمگام در JavaScript

قبل از وارد شدن به این مقاله، شاید بخواهید پایه برخی مفاهیم را یاد بگیرید:

همچنین بر روی راکت بخوانید:

در جهت داشتن یک زمینه خوب، بیایید با آنچه که می‌دانیم شروع کنیم. ممکن است به یاد داشته باشید که Iteratorها با استفاده از Symbol.iterator به عنوان رابطی برای تعریف نحوه تکرار شدن یک آبجکت، چگونه کار می‌کند.

const ponyfoo = {

  [Symbol.iterator]: () => {

    const items = [`p`, `o`, `n`, `y`, `f`, `o`, `o`];

    return {

      next: () => ({

        done: items.length === 0,

        value: items.shift()

      })

    }

  }

}

و این که آبجکت ponyfoo می‌تواند به چند روش مختلف تکرار شود: مانند استفاده از Spread Operator، Array.from یا for…of و چند مورد دیگر.

[...ponyfoo]

// <- [`p`, `o`, `n`, `y`, `f`, `o`, `o`]

Array.from(ponyfoo)

// <- [`p`, `o`, `n`, `y`, `f`, `o`, `o`]



for (const item of ponyfoo) {

  console.log(item)

  // <- `p`

  // <- `o`

  // <- `n`

  // <- `y`

  // <- `f`

  // <- `o`

  // <- `o`

}

قرارداد یک iterator، به متد next از نمونه Symbol.iterator دستور می‌دهد که یک آبجکت را به همراه ویژگی‌های value یا done برگرداند. ویژگی value، مقدار فعلی در sequence مورد نظر را نشان می‌دهد، در حالیکه done به عنوان یک boolean، نشان می‌دهد که sequence مورد نظر به پایان رسیده است یا نه.

در تکرار کننده‌های async، قرارداد کمی تغییر می‌کند: next باید یک Promise را برگرداند که به یک آبجکت شامل ویژگی‌های value و done مقرر می‌کند. به جای استفاده مجدد از Symbol مشابه، یک Symbol.asyncIterator معرفی می‌شود تا تکرار کننده‌های ناهمگام را اعلام کند.

برای هدف نمایش خود، iterable با نام ponfoo‌ می‌توانست به صورت ناهمگام با دو ویرایش کوچک قابل تغییر شود: ما Symbol.iterator را به نفع Symbol.asyncIterator حذف می‌کنیم، و مقدار برگشتی را برای متد next در Promise.resolve جمع‌بندی می‌کنیم، و در نتیجه یک Promise را بر می‌گردانیم.

const ponyfoo = {

  [Symbol.asyncIterator]: () => {

    const items = [`p`, `o`, `n`, `y`, `f`, `o`, `o`];

    return {

      next: () => Promise.resolve({

        done: items.length === 0,

        value: items.shift()

      })

    }

  }

}

به طور طبیعی، این یک مثال کاملا ساختگی بود. یک مثال ساختگی دیگر، می‌تواند یک تابع کاربری باشد که مجموعه‌ای از منابع HTTP را به ترتیب دریافت می‌کند.

const getResources = endpoints => ({

  [Symbol.asyncIterator]: () => ({

    i: 0,

    next () {

      if (endpoints.length <= this.i) {

        return Promise.resolve({ done: true })

      }

      return fetch(endpoints[this.i++])

        .then(response => response.json())

        .then(value => ({ value, done: false }))

    }

  })

})

در جهت هضم یک تکرار کننده async، می‌توانیم از سینتکس for await..of استفاده کنیم که توسط این تکرار کننده‌ها معرفی می‌شود. به این صورت، راه دیگری برای نوشتن کد وجود دارد که همگام به نظر می‌آید، اما به صورت ناهمگام رفتار می‌کند.

const resources = [

  `/api/users`,

  `/api/testers`,

  `/api/hackers`,

  `/api/nsa-backdoor`

];



for await (const data of getResources(resources)) {

  console.log(data);

}

همچنین در این تکرار کننده‌ها، یک تابع مولد async‌ وجود دارد. یک تابع مولد async، درست به مانند یک تابع مولد است، اما از بیانیه‌های await و for await..pf هم پشتیبانی می‌کند.

async function* getResources(endpoints) {

  for (endpoint of endpoints) {

    const response = await fetch(endpoint)

    yield await response.json()

  }

}

مولدهای async وقتی که فراخوانی می‌شوند، یک آبجکت { next, return, throw } را بر می‌گردانند، که متدهایشان به جای برگرداندن مستقیم { next, done }، promiseهایی برای { next, done } را بر می‌گردانند.

شما می‌توانید از مولد async با نام getResources هم دقیقا به روشی که تکرار کننده‌های با گرایش به آبجکت را استفاده می‌کنید، استفاده کنید.

منبع

چه امتیازی برای این مقاله میدهید؟

خیلی بد
بد
متوسط
خوب
عالی
در انتظار ثبت رای

/@er79ka

دیدگاه و پرسش

برای ارسال دیدگاه لازم است وارد شده یا ثبت‌نام کنید ورود یا ثبت‌نام

در حال دریافت نظرات از سرور، لطفا منتظر بمانید

در حال دریافت نظرات از سرور، لطفا منتظر بمانید