স্ট্রীম-নির্দিষ্ট গাইড

স্ট্রিম এপিআই-এর সাহায্যে কীভাবে পঠনযোগ্য, লেখার যোগ্য এবং রূপান্তরিত স্ট্রিমগুলি ব্যবহার করবেন তা শিখুন।

টমাস স্টেইনার
টমাস স্টেইনার

স্ট্রীমস এপিআই আপনাকে নেটওয়ার্কের মাধ্যমে প্রাপ্ত ডেটার স্ট্রীমগুলিকে প্রোগ্রাম্যাটিকভাবে অ্যাক্সেস করতে দেয় বা স্থানীয়ভাবে যেকোন উপায়ে তৈরি করা যায় এবং জাভাস্ক্রিপ্টের মাধ্যমে সেগুলি প্রক্রিয়া করতে দেয়। স্ট্রিমিং এর মধ্যে এমন একটি সংস্থান ভেঙে ফেলা জড়িত যা আপনি পেতে, পাঠাতে বা ছোট খণ্ডে রূপান্তর করতে চান এবং তারপরে এই খণ্ডগুলিকে বিট করে প্রক্রিয়াকরণ করে। ওয়েবপেজে দেখানোর জন্য HTML বা ভিডিওর মতো সম্পদ গ্রহণ করার সময় স্ট্রিমিং এমন কিছু যা ব্রাউজাররা করে থাকে, 2015 সালে স্ট্রিমগুলির সাথে fetch আগে এই ক্ষমতা জাভাস্ক্রিপ্টে উপলব্ধ ছিল না।

পূর্বে, আপনি যদি কোনো ধরনের রিসোর্স প্রসেস করতে চান (এটি একটি ভিডিও, বা একটি টেক্সট ফাইল, ইত্যাদি), আপনাকে সম্পূর্ণ ফাইলটি ডাউনলোড করতে হবে, এটিকে একটি উপযুক্ত বিন্যাসে ডিসিরিয়ালাইজ করার জন্য অপেক্ষা করতে হবে, এবং তারপরে প্রক্রিয়া করুন এটা জাভাস্ক্রিপ্টে স্ট্রীম উপলব্ধ হওয়ার সাথে সাথে, এই সমস্ত পরিবর্তন হয়। বাফার, স্ট্রিং বা ব্লব তৈরি করার প্রয়োজন ছাড়াই আপনি এখন ক্লায়েন্টে উপলব্ধ হওয়ার সাথে সাথে জাভাস্ক্রিপ্টের সাথে ক্রমাগতভাবে কাঁচা ডেটা প্রক্রিয়া করতে পারেন। এটি বেশ কয়েকটি ব্যবহারের ক্ষেত্রে আনলক করে, যার মধ্যে কয়েকটি আমি নীচে তালিকাভুক্ত করি:

  • ভিডিও ইফেক্ট: ট্রান্সফর্ম স্ট্রিমের মাধ্যমে একটি পঠনযোগ্য ভিডিও স্ট্রীম পাইপ করা যা রিয়েল টাইমে প্রভাব প্রয়োগ করে।
  • ডেটা (ডি) কম্প্রেশন: একটি ট্রান্সফর্ম স্ট্রিমের মাধ্যমে একটি ফাইল স্ট্রীমকে পাইপ করা যা বেছে বেছে (ডি) কম্প্রেস করে।
  • ইমেজ ডিকোডিং: একটি ট্রান্সফর্ম স্ট্রীমের মাধ্যমে একটি HTTP প্রতিক্রিয়া স্ট্রীম পাইপ করা যা বাইটকে বিটম্যাপ ডেটাতে ডিকোড করে এবং তারপরে অন্য ট্রান্সফর্ম স্ট্রিমের মাধ্যমে যা বিটম্যাপগুলিকে PNG তে অনুবাদ করে৷ যদি কোনও পরিষেবা কর্মীর fetch হ্যান্ডলারের ভিতরে ইনস্টল করা থাকে তবে এটি আপনাকে স্বচ্ছভাবে AVIF এর মতো নতুন চিত্র বিন্যাসগুলিকে পলিফিল করতে দেয়৷

ব্রাউজার সমর্থন

পঠনযোগ্য স্ট্রীম এবং লিখনযোগ্য স্ট্রীম

ব্রাউজার সমর্থন

  • 43
  • 14
  • 65
  • 10.1

উৎস

ট্রান্সফর্ম স্ট্রিম

ব্রাউজার সমর্থন

  • 67
  • 79
  • 102
  • 14.1

উৎস

মুল ধারণা

আমি বিভিন্ন ধরণের স্ট্রীম সম্পর্কে বিশদে যাওয়ার আগে, আমাকে কিছু মূল ধারণার সাথে পরিচয় করিয়ে দেওয়া যাক।

খণ্ড

একটি খণ্ড হল ডেটার একক অংশ যা একটি স্ট্রিম থেকে লেখা বা পড়া হয়। এটা যে কোন ধরনের হতে পারে; স্ট্রীম এমনকি বিভিন্ন ধরনের খণ্ড ধারণ করতে পারে. বেশিরভাগ সময়, একটি খণ্ড একটি প্রদত্ত প্রবাহের জন্য ডেটার সর্বাধিক পারমাণবিক একক হবে না। উদাহরণস্বরূপ, একটি বাইট স্ট্রীমে একক বাইটের পরিবর্তে 16 KiB Uint8Array ইউনিট সমন্বিত খণ্ড থাকতে পারে।

পঠনযোগ্য প্রবাহ

একটি পঠনযোগ্য স্ট্রিম ডেটার একটি উৎসকে উপস্থাপন করে যেখান থেকে আপনি পড়তে পারেন। অন্য কথায়, ডেটা একটি পঠনযোগ্য স্ট্রিম থেকে বেরিয়ে আসে । কংক্রিটভাবে, একটি পঠনযোগ্য স্ট্রিম হল ReadableStream ক্লাসের একটি উদাহরণ।

লেখার যোগ্য প্রবাহ

একটি লিখনযোগ্য স্ট্রিম ডেটার জন্য একটি গন্তব্য প্রতিনিধিত্ব করে যেখানে আপনি লিখতে পারেন। অন্য কথায়, ডেটা একটি লিখনযোগ্য প্রবাহে যায় । নির্দিষ্টভাবে, একটি লিখনযোগ্য স্ট্রিম হল WritableStream ক্লাসের একটি উদাহরণ।

স্ট্রীম রূপান্তর

একটি রূপান্তর স্ট্রীম একজোড়া স্ট্রীম নিয়ে গঠিত: একটি লিখনযোগ্য স্ট্রীম, এটির লিখনযোগ্য দিক হিসাবে পরিচিত, এবং একটি পাঠযোগ্য স্ট্রীম, এটির পাঠযোগ্য দিক হিসাবে পরিচিত। এটির জন্য একটি বাস্তব-বিশ্ব রূপক হবে একজন যুগপৎ দোভাষী যিনি এক ভাষা থেকে অন্য ভাষাতে অন-দ্য-ফ্লাই অনুবাদ করেন। ট্রান্সফর্ম স্ট্রীমের জন্য নির্দিষ্ট পদ্ধতিতে, লিখনযোগ্য দিকে লেখার ফলে পঠনযোগ্য দিক থেকে পড়ার জন্য নতুন ডেটা উপলব্ধ করা হয়। সুনির্দিষ্টভাবে, writable সম্পত্তি এবং একটি readable সম্পত্তি সহ যেকোন বস্তু রূপান্তর স্ট্রীম হিসাবে কাজ করতে পারে। যাইহোক, স্ট্যান্ডার্ড TransformStream ক্লাস এমন একটি জুটি তৈরি করা সহজ করে তোলে যা সঠিকভাবে আটকে আছে।

পাইপ চেইন

স্ট্রিমগুলি প্রাথমিকভাবে একে অপরের সাথে পাইপ করে ব্যবহার করা হয়। পঠনযোগ্য স্ট্রীমের pipeTo() পদ্ধতি ব্যবহার করে একটি পঠনযোগ্য স্ট্রীম সরাসরি একটি লিখনযোগ্য স্ট্রীমে পাইপ করা যেতে পারে, অথবা এটি পঠনযোগ্য স্ট্রিমের pipeThrough() পদ্ধতি ব্যবহার করে প্রথমে এক বা একাধিক ট্রান্সফর্ম স্ট্রিমের মাধ্যমে পাইপ করা যেতে পারে। এইভাবে একসাথে পাইপ করা স্ট্রিমগুলির একটি সেটকে পাইপ চেইন হিসাবে উল্লেখ করা হয়।

ফিরতি চাপ

একবার একটি পাইপ চেইন তৈরি হয়ে গেলে, এটি কত দ্রুত অংশগুলি এর মধ্য দিয়ে প্রবাহিত হওয়া উচিত সে সম্পর্কে সংকেত প্রচার করবে। যদি শৃঙ্খলের কোনো ধাপ এখনও খণ্ডগুলি গ্রহণ করতে না পারে, তবে এটি পাইপ চেইনের মাধ্যমে পিছনের দিকে একটি সংকেত প্রচার করে, যতক্ষণ না শেষ পর্যন্ত মূল উত্সকে এত দ্রুত খণ্ডগুলি উত্পাদন বন্ধ করতে বলা হয়। প্রবাহ স্বাভাবিক করার এই প্রক্রিয়াটিকে ব্যাকপ্রেশার বলে।

টিইং

একটি পঠনযোগ্য স্ট্রীম এর tee() পদ্ধতি ব্যবহার করে teed (একটি বড় হাতের 'T' আকৃতি অনুসারে নামকরণ করা হয়েছে) হতে পারে। এটি স্ট্রিমটিকে লক করবে, অর্থাৎ, এটিকে আর সরাসরি ব্যবহারযোগ্য করে তুলবে না; যাইহোক, এটি দুটি নতুন স্ট্রীম তৈরি করবে, যাকে বলা হয় শাখা, যা স্বাধীনভাবে খাওয়া যেতে পারে। টিইং করাও গুরুত্বপূর্ণ কারণ স্ট্রীম রিওয়াউন্ড বা রিস্টার্ট করা যাবে না, এই বিষয়ে পরে আরও কিছু।

একটি পাইপ চেইনের ডায়াগ্রাম যার মধ্যে একটি পঠনযোগ্য স্ট্রীম রয়েছে যা একটি কল থেকে ফেচ এপিআইতে আসছে যা তারপর একটি ট্রান্সফর্ম স্ট্রিমের মাধ্যমে পাইপ করা হয় যার আউটপুট টিড হয় এবং তারপর প্রথম ফলস্বরূপ পঠনযোগ্য স্ট্রিমের জন্য ব্রাউজারে এবং পরিষেবা কর্মী ক্যাশে পাঠানো হয় দ্বিতীয় ফলে পঠনযোগ্য স্ট্রীম।
একটি পাইপ চেইন।

একটি পঠনযোগ্য স্রোতের মেকানিক্স

একটি পঠনযোগ্য স্ট্রিম হল একটি ডেটা উৎস যা জাভাস্ক্রিপ্টে একটি ReadableStream অবজেক্ট দ্বারা উপস্থাপিত হয় যা একটি অন্তর্নিহিত উৎস থেকে প্রবাহিত হয়। ReadableStream() কনস্ট্রাক্টর প্রদত্ত হ্যান্ডলার থেকে একটি পঠনযোগ্য স্ট্রিম অবজেক্ট তৈরি করে এবং ফেরত দেয়। দুটি ধরণের অন্তর্নিহিত উত্স রয়েছে:

  • আপনি যখন সেগুলি অ্যাক্সেস করেন তখন পুশ উত্সগুলি ক্রমাগত ডেটা আপনার দিকে ঠেলে দেয় এবং স্ট্রিমে অ্যাক্সেস শুরু করা, বিরতি দেওয়া বা বাতিল করা আপনার উপর নির্ভর করে৷ উদাহরণগুলির মধ্যে লাইভ ভিডিও স্ট্রিম, সার্ভার-প্রেরিত ইভেন্ট বা WebSockets অন্তর্ভুক্ত।
  • পুল সোর্সগুলির সাথে সংযুক্ত হওয়ার পরে আপনাকে তাদের থেকে স্পষ্টভাবে ডেটার অনুরোধ করতে হবে৷ উদাহরণ হল fetch() বা XMLHttpRequest কলের মাধ্যমে HTTP অপারেশন।

স্ট্রিম ডেটা ক্রমিকভাবে ছোট ছোট টুকরোতে পড়া হয় যাকে বলা হয় খণ্ড । একটি স্রোতে স্থাপন করা অংশগুলিকে সারিবদ্ধ বলা হয়। এর মানে তারা পড়ার জন্য প্রস্তুত একটি সারিতে অপেক্ষা করছে। একটি অভ্যন্তরীণ সারি এখনও পড়া হয়নি এমন খণ্ডগুলির ট্র্যাক রাখে৷

একটি সারিবদ্ধ কৌশল হল একটি বস্তু যা নির্ধারণ করে কিভাবে একটি স্ট্রীম তার অভ্যন্তরীণ সারির অবস্থার উপর ভিত্তি করে ব্যাকপ্রেশার সংকেত দেবে। সারিবদ্ধ কৌশল প্রতিটি খণ্ডে একটি আকার নির্ধারণ করে এবং সারিতে থাকা সমস্ত খণ্ডের মোট আকারকে একটি নির্দিষ্ট সংখ্যার সাথে তুলনা করে, যা উচ্চ জল চিহ্ন নামে পরিচিত।

স্রোতের ভিতরের অংশগুলি একজন পাঠক পড়েন। এই পাঠক একবারে এক খণ্ড ডেটা পুনরুদ্ধার করে, আপনি এটিতে যে কোনও ধরণের অপারেশন করতে চান তা করার অনুমতি দেয়৷ পাঠক এবং অন্যান্য প্রক্রিয়াকরণ কোড যা এটির সাথে যায় তাকে ভোক্তা বলা হয়।

এই প্রসঙ্গে পরবর্তী গঠনটিকে একটি নিয়ামক বলা হয়। প্রতিটি পঠনযোগ্য স্ট্রীমের একটি সংশ্লিষ্ট নিয়ামক থাকে যা নাম অনুসারেই আপনাকে স্ট্রীম নিয়ন্ত্রণ করতে দেয়।

শুধুমাত্র একজন পাঠক একবারে একটি স্ট্রিম পড়তে পারেন; যখন একটি পাঠক তৈরি হয় এবং একটি স্ট্রিম পড়তে শুরু করে (অর্থাৎ, একটি সক্রিয় পাঠক হয়ে ওঠে), এটি এটিতে লক করা হয়। আপনি যদি অন্য পাঠক আপনার স্ট্রিম পড়ার দায়িত্ব নিতে চান তবে অন্য কিছু করার আগে আপনাকে সাধারণত প্রথম পাঠককে ছেড়ে দিতে হবে (যদিও আপনি টি স্ট্রিম করতে পারেন)।

একটি পঠনযোগ্য স্ট্রীম তৈরি করা হচ্ছে

আপনি এর কন্সট্রাকটর ReadableStream() কল করে একটি পঠনযোগ্য স্ট্রীম তৈরি করুন। কনস্ট্রাক্টরের underlyingSource একটি ঐচ্ছিক যুক্তি রয়েছে সোর্স, যা পদ্ধতি এবং বৈশিষ্ট্য সহ একটি বস্তুকে উপস্থাপন করে যা সংজ্ঞায়িত করে যে কীভাবে নির্মিত স্ট্রিম উদাহরণটি আচরণ করবে।

underlyingSource

এটি নিম্নলিখিত ঐচ্ছিক, বিকাশকারী-সংজ্ঞায়িত পদ্ধতিগুলি ব্যবহার করতে পারে:

  • start(controller) : অবজেক্টটি তৈরি হলে অবিলম্বে কল করা হয়। পদ্ধতিটি স্ট্রিম উত্স অ্যাক্সেস করতে পারে এবং স্ট্রিম কার্যকারিতা সেট আপ করার জন্য প্রয়োজনীয় অন্য কিছু করতে পারে। যদি এই প্রক্রিয়াটি অ্যাসিঙ্ক্রোনাসভাবে করা হয়, তবে পদ্ধতিটি সাফল্য বা ব্যর্থতার সংকেত দেওয়ার প্রতিশ্রুতি ফিরিয়ে দিতে পারে। এই পদ্ধতিতে পাস করা controller প্যারামিটার হল একটি ReadableStreamDefaultController
  • pull(controller) : আরো খণ্ড আনা হয় হিসাবে স্ট্রীম নিয়ন্ত্রণ করতে ব্যবহার করা যেতে পারে. যতক্ষণ না স্ট্রিমের অভ্যন্তরীণ সারি অংশগুলি পূর্ণ না হয়, যতক্ষণ না সারিটি তার উচ্চ জলের চিহ্নে পৌঁছায় ততক্ষণ পর্যন্ত এটিকে বারবার বলা হয়। যদি pull() কল করার ফলাফল একটি প্রতিশ্রুতি হয়, তাহলে প্রতিশ্রুতি পূরণ না হওয়া পর্যন্ত pull() আবার কল করা হবে না। প্রতিশ্রুতি প্রত্যাখ্যান করলে, প্রবাহটি ভুল হয়ে যাবে।
  • cancel(reason) : যখন স্ট্রীম গ্রাহক স্ট্রিম বাতিল করে তখন বলা হয়।
const readableStream = new ReadableStream({
  start(controller) {
    /* … */
  },

  pull(controller) {
    /* … */
  },

  cancel(reason) {
    /* … */
  },
});

ReadableStreamDefaultController নিম্নলিখিত পদ্ধতি সমর্থন করে:

/* … */
start(controller) {
  controller.enqueue('The first chunk!');
},
/* … */

queuingStrategy

দ্বিতীয়, একইভাবে ঐচ্ছিক, ReadableStream() কনস্ট্রাক্টরের আর্গুমেন্ট হল queuingStrategy । এটি একটি বস্তু যা ঐচ্ছিকভাবে স্ট্রিমের জন্য একটি সারিবদ্ধ কৌশল নির্ধারণ করে, যা দুটি পরামিতি নেয়:

  • highWaterMark : একটি অ-নেতিবাচক সংখ্যা যা এই সারিবদ্ধ কৌশল ব্যবহার করে স্রোতের উচ্চ জলের চিহ্ন নির্দেশ করে।
  • size(chunk) : একটি ফাংশন যা গণনা করে এবং প্রদত্ত খণ্ড মানের সসীম অ-নেতিবাচক আকার প্রদান করে। উপযুক্ত ReadableStreamDefaultController.desiredSize সম্পত্তির মাধ্যমে প্রকাশ করে ব্যাকপ্রেশার নির্ধারণ করতে ফলাফলটি ব্যবহার করা হয়। যখন অন্তর্নিহিত উৎসের pull() পদ্ধতি বলা হয় তখন এটি পরিচালনা করে।
const readableStream = new ReadableStream({
    /* … */
  },
  {
    highWaterMark: 10,
    size(chunk) {
      return chunk.length;
    },
  },
);

getReader() এবং read() পদ্ধতি

একটি পঠনযোগ্য স্ট্রীম থেকে পড়তে, আপনার একটি পাঠক প্রয়োজন, যা একটি ReadableStreamDefaultReader হবে। ReadableStream ইন্টারফেসের getReader() পদ্ধতি একটি পাঠক তৈরি করে এবং এটিতে স্ট্রীম লক করে। স্ট্রীমটি লক থাকা অবস্থায়, এটি রিলিজ না হওয়া পর্যন্ত অন্য কোন পাঠক অর্জন করা যাবে না।

ReadableStreamDefaultReader ইন্টারফেসের read() পদ্ধতিটি স্ট্রীমের অভ্যন্তরীণ সারিতে পরবর্তী অংশে অ্যাক্সেস প্রদান করার প্রতিশ্রুতি প্রদান করে। এটি প্রবাহের অবস্থার উপর নির্ভর করে ফলাফলের সাথে পূর্ণ বা প্রত্যাখ্যান করে। বিভিন্ন সম্ভাবনা নিম্নরূপ:

  • যদি একটি খণ্ড পাওয়া যায়, প্রতিশ্রুতি ফর্মের একটি বস্তুর সাথে পূরণ করা হবে
    { value: chunk, done: false }
  • যদি প্রবাহ বন্ধ হয়ে যায়, তবে প্রতিশ্রুতিটি ফর্মের একটি বস্তুর সাথে পূরণ করা হবে
    { value: undefined, done: true }
  • স্ট্রীম ত্রুটিপূর্ণ হয়ে গেলে, প্রাসঙ্গিক ত্রুটির সাথে প্রতিশ্রুতি প্রত্যাখ্যান করা হবে।
const reader = readableStream.getReader();
while (true) {
  const { done, value } = await reader.read();
  if (done) {
    console.log('The stream is done.');
    break;
  }
  console.log('Just read a chunk:', value);
}

locked সম্পত্তি

আপনি একটি পাঠযোগ্য স্ট্রীম এর ReadableStream.locked সম্পত্তি অ্যাক্সেস করে লক করা আছে কিনা তা পরীক্ষা করতে পারেন৷

const locked = readableStream.locked;
console.log(`The stream is ${locked ? 'indeed' : 'not'} locked.`);

পঠনযোগ্য স্ট্রিম কোড নমুনা

নীচের কোড নমুনা কর্মের সমস্ত পদক্ষেপ দেখায়। আপনি প্রথমে একটি ReadableStream তৈরি করুন যা এর underlyingSource আর্গুমেন্টে (অর্থাৎ, TimestampSource ক্লাস) একটি start() পদ্ধতি সংজ্ঞায়িত করে। এই পদ্ধতিটি স্ট্রিমের controller দশ সেকেন্ডের মধ্যে প্রতি সেকেন্ডে একটি টাইমস্ট্যাম্প enqueue() করতে বলে। অবশেষে, এটি কন্ট্রোলারকে স্ট্রীম close() করতে বলে। আপনি getReader() পদ্ধতির মাধ্যমে একটি পাঠক তৈরি করে এবং স্ট্রিমটি done না হওয়া পর্যন্ত read() কল করে এই স্ট্রীমটি ব্যবহার করেন।

class TimestampSource {
  #interval

  start(controller) {
    this.#interval = setInterval(() => {
      const string = new Date().toLocaleTimeString();
      // Add the string to the stream.
      controller.enqueue(string);
      console.log(`Enqueued ${string}`);
    }, 1_000);

    setTimeout(() => {
      clearInterval(this.#interval);
      // Close the stream after 10s.
      controller.close();
    }, 10_000);
  }

  cancel() {
    // This is called if the reader cancels.
    clearInterval(this.#interval);
  }
}

const stream = new ReadableStream(new TimestampSource());

async function concatStringStream(stream) {
  let result = '';
  const reader = stream.getReader();
  while (true) {
    // The `read()` method returns a promise that
    // resolves when a value has been received.
    const { done, value } = await reader.read();
    // Result objects contain two properties:
    // `done`  - `true` if the stream has already given you all its data.
    // `value` - Some data. Always `undefined` when `done` is `true`.
    if (done) return result;
    result += value;
    console.log(`Read ${result.length} characters so far`);
    console.log(`Most recently read chunk: ${value}`);
  }
}
concatStringStream(stream).then((result) => console.log('Stream complete', result));

অ্যাসিঙ্ক্রোনাস পুনরাবৃত্তি

প্রতিটি read() লুপ পুনরাবৃত্তির উপর পরীক্ষা করা যদি স্ট্রিমটি done তবে সবচেয়ে সুবিধাজনক API নাও হতে পারে। সৌভাগ্যবশত শীঘ্রই এটি করার একটি ভাল উপায় থাকবে: অ্যাসিঙ্ক্রোনাস পুনরাবৃত্তি।

for await (const chunk of stream) {
  console.log(chunk);
}

আজকে অ্যাসিঙ্ক্রোনাস পুনরাবৃত্তি ব্যবহার করার একটি সমাধান হল একটি পলিফিলের সাথে আচরণটি বাস্তবায়ন করা।

if (!ReadableStream.prototype[Symbol.asyncIterator]) {
  ReadableStream.prototype[Symbol.asyncIterator] = async function* () {
    const reader = this.getReader();
    try {
      while (true) {
        const {done, value} = await reader.read();
        if (done) {
          return;
          }
        yield value;
      }
    }
    finally {
      reader.releaseLock();
    }
  }
}

একটি পঠনযোগ্য স্ট্রিম টিইং

ReadableStream ইন্টারফেসের tee() পদ্ধতিটি বর্তমান পঠনযোগ্য স্ট্রীমকে টিস করে, নতুন ReadableStream দৃষ্টান্ত হিসাবে দুটি ফলস্বরূপ শাখা সমন্বিত একটি দুই-উপাদান অ্যারে ফিরিয়ে দেয়। এটি দুই পাঠককে একই সাথে একটি স্ট্রিম পড়তে দেয়। আপনি এটি করতে পারেন, উদাহরণস্বরূপ, একজন পরিষেবা কর্মী যদি আপনি সার্ভার থেকে একটি প্রতিক্রিয়া আনতে চান এবং এটি ব্রাউজারে স্ট্রিম করতে চান তবে এটি পরিষেবা কর্মী ক্যাশেও স্ট্রিম করতে পারেন৷ যেহেতু একটি রেসপন্স বডি একবারের বেশি খাওয়া যাবে না, তাই এটি করার জন্য আপনার দুটি কপি প্রয়োজন। স্ট্রীম বাতিল করতে, আপনাকে তারপর উভয় ফলের শাখা বাতিল করতে হবে। একটি স্ট্রীম টিইং সাধারণত সময়কালের জন্য এটি লক করবে, অন্য পাঠকদের এটি লক করা থেকে বাধা দেবে৷

const readableStream = new ReadableStream({
  start(controller) {
    // Called by constructor.
    console.log('[start]');
    controller.enqueue('a');
    controller.enqueue('b');
    controller.enqueue('c');
  },
  pull(controller) {
    // Called `read()` when the controller's queue is empty.
    console.log('[pull]');
    controller.enqueue('d');
    controller.close();
  },
  cancel(reason) {
    // Called when the stream is canceled.
    console.log('[cancel]', reason);
  },
});

// Create two `ReadableStream`s.
const [streamA, streamB] = readableStream.tee();

// Read streamA iteratively one by one. Typically, you
// would not do it this way, but you certainly can.
const readerA = streamA.getReader();
console.log('[A]', await readerA.read()); //=> {value: "a", done: false}
console.log('[A]', await readerA.read()); //=> {value: "b", done: false}
console.log('[A]', await readerA.read()); //=> {value: "c", done: false}
console.log('[A]', await readerA.read()); //=> {value: "d", done: false}
console.log('[A]', await readerA.read()); //=> {value: undefined, done: true}

// Read streamB in a loop. This is the more common way
// to read data from the stream.
const readerB = streamB.getReader();
while (true) {
  const result = await readerB.read();
  if (result.done) break;
  console.log('[B]', result);
}

পঠনযোগ্য বাইট স্ট্রীম

বাইট প্রতিনিধিত্বকারী স্ট্রিমগুলির জন্য, পঠনযোগ্য স্ট্রিমের একটি বর্ধিত সংস্করণ প্রদান করা হয় যাতে বাইটগুলিকে দক্ষতার সাথে পরিচালনা করা হয়, বিশেষ করে কপিগুলিকে ছোট করে। বাইট স্ট্রীমগুলি আপনার-নিজের-বাফার (BYOB) পাঠকদের অধিগ্রহণ করার অনুমতি দেয়। ডিফল্ট বাস্তবায়ন বিভিন্ন আউটপুট দিতে পারে যেমন WebSockets এর ক্ষেত্রে স্ট্রিং বা অ্যারে বাফার, যেখানে বাইট স্ট্রীম বাইট আউটপুটের গ্যারান্টি দেয়। উপরন্তু, BYOB পাঠকদের স্থিতিশীলতার সুবিধা রয়েছে। এর কারণ হল যদি একটি বাফার বিচ্ছিন্ন হয়, এটি গ্যারান্টি দিতে পারে যে কেউ একই বাফারে দুবার লিখবে না, তাই রেসের অবস্থা এড়িয়ে যায়। BYOB পাঠকরা ব্রাউজারকে আবর্জনা সংগ্রহ চালানোর প্রয়োজনের সংখ্যা কমাতে পারে, কারণ এটি বাফারগুলি পুনরায় ব্যবহার করতে পারে।

একটি পঠনযোগ্য বাইট স্ট্রিম তৈরি করা হচ্ছে

আপনি ReadableStream() কনস্ট্রাক্টরে একটি অতিরিক্ত type প্যারামিটার পাস করে একটি পঠনযোগ্য বাইট স্ট্রিম তৈরি করতে পারেন।

new ReadableStream({ type: 'bytes' });

underlyingSource

একটি পঠনযোগ্য বাইট স্ট্রীমের অন্তর্নিহিত উৎসকে ম্যানিপুলেট করার জন্য একটি ReadableByteStreamController দেওয়া হয়। এর ReadableByteStreamController.enqueue() পদ্ধতিটি একটি chunk আর্গুমেন্ট নেয় যার মান একটি ArrayBufferViewReadableByteStreamController.byobRequest প্রপার্টি বর্তমান BYOB পুল রিকোয়েস্ট রিটার্ন করে, অথবা যদি কোনটি না থাকে তাহলে শূন্য। অবশেষে, ReadableByteStreamController.desiredSize প্রপার্টি নিয়ন্ত্রিত স্ট্রীমের অভ্যন্তরীণ সারি পূরণ করতে পছন্দসই আকার প্রদান করে।

queuingStrategy

দ্বিতীয়, একইভাবে ঐচ্ছিক, ReadableStream() কনস্ট্রাক্টরের আর্গুমেন্ট হল queuingStrategy । এটি একটি বস্তু যা ঐচ্ছিকভাবে স্ট্রিমের জন্য একটি সারিবদ্ধ কৌশল নির্ধারণ করে, যা একটি প্যারামিটার নেয়:

  • highWaterMark : এই সারিবদ্ধ কৌশল ব্যবহার করে স্রোতের উচ্চ জলের চিহ্ন নির্দেশ করে একটি অ-নেতিবাচক সংখ্যা বাইট। এটি উপযুক্ত ReadableByteStreamController.desiredSize সম্পত্তির মাধ্যমে প্রকাশ করে ব্যাকপ্রেশার নির্ধারণ করতে ব্যবহৃত হয়। যখন অন্তর্নিহিত উৎসের pull() পদ্ধতি বলা হয় তখন এটি পরিচালনা করে।

getReader() এবং read() পদ্ধতি

তারপরে আপনি সেই অনুযায়ী mode প্যারামিটার সেট করে একটি ReadableStreamBYOBReader এ অ্যাক্সেস পেতে পারেন: ReadableStream.getReader({ mode: "byob" }) । এটি অনুলিপিগুলি এড়াতে বাফার বরাদ্দের উপর আরও সুনির্দিষ্ট নিয়ন্ত্রণের অনুমতি দেয়। বাইট স্ট্রীম থেকে পড়তে, আপনাকে ReadableStreamBYOBReader.read(view) কল করতে হবে, যেখানে view একটি ArrayBufferView

পঠনযোগ্য বাইট স্ট্রিম কোড নমুনা

const reader = readableStream.getReader({ mode: "byob" });

let startingAB = new ArrayBuffer(1_024);
const buffer = await readInto(startingAB);
console.log("The first 1024 bytes, or less:", buffer);

async function readInto(buffer) {
  let offset = 0;

  while (offset < buffer.byteLength) {
    const { value: view, done } =
        await reader.read(new Uint8Array(buffer, offset, buffer.byteLength - offset));
    buffer = view.buffer;
    if (done) {
      break;
    }
    offset += view.byteLength;
  }

  return buffer;
}

নিম্নলিখিত ফাংশন পঠনযোগ্য বাইট স্ট্রীম প্রদান করে যা এলোমেলোভাবে তৈরি করা অ্যারের দক্ষ শূন্য-কপি পড়ার অনুমতি দেয়। 1,024 এর একটি পূর্বনির্ধারিত খণ্ড আকার ব্যবহার করার পরিবর্তে, এটি সম্পূর্ণ নিয়ন্ত্রণের অনুমতি দিয়ে বিকাশকারী দ্বারা সরবরাহ করা বাফার পূরণ করার চেষ্টা করে।

const DEFAULT_CHUNK_SIZE = 1_024;

function makeReadableByteStream() {
  return new ReadableStream({
    type: 'bytes',

    pull(controller) {
      // Even when the consumer is using the default reader,
      // the auto-allocation feature allocates a buffer and
      // passes it to us via `byobRequest`.
      const view = controller.byobRequest.view;
      view = crypto.getRandomValues(view);
      controller.byobRequest.respond(view.byteLength);
    },

    autoAllocateChunkSize: DEFAULT_CHUNK_SIZE,
  });
}

একটি লিখনযোগ্য প্রবাহের যান্ত্রিকতা

একটি লিখনযোগ্য স্ট্রীম হল একটি গন্তব্য যেখানে আপনি ডাটা লিখতে পারেন, যা জাভাস্ক্রিপ্টে একটি WritableStream অবজেক্ট দ্বারা উপস্থাপিত হয়। এটি একটি অন্তর্নিহিত সিঙ্কের শীর্ষে একটি বিমূর্ততা হিসাবে কাজ করে — একটি নিম্ন-স্তরের I/O সিঙ্ক যেখানে কাঁচা ডেটা লেখা হয়।

ডেটা একটি লেখকের মাধ্যমে স্ট্রীমে লেখা হয়, এক সময়ে এক খণ্ড। একটি খণ্ড অনেকগুলি রূপ নিতে পারে, ঠিক যেমন একটি পাঠকের খণ্ডগুলি। লেখার জন্য প্রস্তুত খণ্ডগুলি তৈরি করতে আপনি যেকোন কোড ব্যবহার করতে পারেন; লেখক এবং সংশ্লিষ্ট কোডকে প্রযোজক বলা হয়।

যখন একজন লেখক তৈরি হয় এবং একটি স্রোতে (একজন সক্রিয় লেখক ) লিখতে শুরু করে, তখন এটিকে তালাবদ্ধ বলা হয়। এক সময়ে শুধুমাত্র একজন লেখকই লিখতে পারেন একটি লিখনযোগ্য প্রবাহে। আপনি যদি অন্য লেখক আপনার স্ট্রীমে লেখা শুরু করতে চান, তবে আপনাকে সাধারণত এটি প্রকাশ করতে হবে, আগে আপনি এটিতে অন্য লেখককে সংযুক্ত করুন।

একটি অভ্যন্তরীণ সারি সেই অংশগুলির ট্র্যাক রাখে যা স্ট্রীমে লেখা হয়েছে কিন্তু এখনও অন্তর্নিহিত সিঙ্ক দ্বারা প্রক্রিয়া করা হয়নি।

একটি সারিবদ্ধ কৌশল হল একটি বস্তু যা নির্ধারণ করে কিভাবে একটি স্ট্রীম তার অভ্যন্তরীণ সারির অবস্থার উপর ভিত্তি করে ব্যাকপ্রেশার সংকেত দেবে। সারিবদ্ধ কৌশল প্রতিটি খণ্ডে একটি আকার নির্ধারণ করে এবং সারিতে থাকা সমস্ত খণ্ডের মোট আকারকে একটি নির্দিষ্ট সংখ্যার সাথে তুলনা করে, যা উচ্চ জল চিহ্ন নামে পরিচিত।

চূড়ান্ত গঠন একটি নিয়ামক বলা হয়. প্রতিটি লেখার যোগ্য স্ট্রীমের একটি সংশ্লিষ্ট নিয়ামক থাকে যা আপনাকে স্ট্রিম নিয়ন্ত্রণ করতে দেয় (উদাহরণস্বরূপ, এটি বাতিল করতে)।

একটি লিখনযোগ্য স্ট্রীম তৈরি করা হচ্ছে

স্ট্রীমস API-এর WritableStream ইন্টারফেস একটি গন্তব্যে স্ট্রিমিং ডেটা লেখার জন্য একটি আদর্শ বিমূর্ততা প্রদান করে, যা একটি সিঙ্ক নামে পরিচিত। এই বস্তুটি অন্তর্নির্মিত ব্যাকপ্রেশার এবং সারিবদ্ধতার সাথে আসে। আপনি এর কন্সট্রাকটর WritableStream() কল করে একটি লিখনযোগ্য স্ট্রীম তৈরি করেন। এটিতে একটি ঐচ্ছিক underlyingSink পরামিতি রয়েছে, যা পদ্ধতি এবং বৈশিষ্ট্য সহ একটি বস্তুর প্রতিনিধিত্ব করে যা সংজ্ঞায়িত করে কিভাবে নির্মিত স্ট্রিম উদাহরণটি আচরণ করবে।

underlyingSink

underlyingSink নিম্নলিখিত ঐচ্ছিক, বিকাশকারী-সংজ্ঞায়িত পদ্ধতিগুলি অন্তর্ভুক্ত থাকতে পারে। কিছু পদ্ধতিতে পাস করা controller প্যারামিটার হল একটি WritableStreamDefaultController

  • start(controller) : অবজেক্টটি তৈরি হলেই এই পদ্ধতিটিকে অবিলম্বে বলা হয়। এই পদ্ধতির বিষয়বস্তু অন্তর্নিহিত সিঙ্ক অ্যাক্সেস পেতে লক্ষ্য করা উচিত. যদি এই প্রক্রিয়াটি অ্যাসিঙ্ক্রোনাসভাবে করা হয় তবে এটি সাফল্য বা ব্যর্থতার সংকেত দেওয়ার প্রতিশ্রুতি ফিরিয়ে দিতে পারে।
  • write(chunk, controller) : এই পদ্ধতিটি বলা হবে যখন ডেটার একটি নতুন খণ্ড ( chunk প্যারামিটারে নির্দিষ্ট) অন্তর্নিহিত সিঙ্কে লেখার জন্য প্রস্তুত। এটি লেখার ক্রিয়াকলাপের সাফল্য বা ব্যর্থতার সংকেত দেওয়ার প্রতিশ্রুতি ফিরিয়ে দিতে পারে। এই পদ্ধতিটি শুধুমাত্র পূর্ববর্তী লেখাগুলি সফল হওয়ার পরেই বলা হবে, এবং স্ট্রীম বন্ধ বা বাতিল হওয়ার পরে কখনই নয়।
  • close(controller) : অ্যাপটি যদি সংকেত দেয় যে এটি স্ট্রীমে অংশ লেখা শেষ করেছে তাহলে এই পদ্ধতিটিকে বলা হবে। অন্তর্নিহিত সিঙ্কে লেখাগুলি চূড়ান্ত করার জন্য এবং এটিতে অ্যাক্সেস ছেড়ে দেওয়ার জন্য বিষয়বস্তুগুলির যা করা দরকার তা করা উচিত। এই প্রক্রিয়াটি অ্যাসিঙ্ক্রোনাস হলে, এটি সাফল্য বা ব্যর্থতার সংকেত দেওয়ার প্রতিশ্রুতি ফিরিয়ে দিতে পারে। সমস্ত সারিবদ্ধ লেখা সফল হওয়ার পরেই এই পদ্ধতিটি কল করা হবে।
  • abort(reason) : এই পদ্ধতিটিকে বলা হবে যদি অ্যাপটি সংকেত দেয় যে এটি হঠাৎ করে স্ট্রীমটি বন্ধ করে একটি ত্রুটিযুক্ত অবস্থায় রাখতে চায়। এটি close() এর মত যেকোনও হোল্ড রিসোর্স পরিষ্কার করতে পারে, কিন্তু রাইটগুলি সারিবদ্ধ থাকলেও abort() বলা হবে। সেই খণ্ডগুলো ফেলে দেওয়া হবে। এই প্রক্রিয়াটি অ্যাসিঙ্ক্রোনাস হলে, এটি সাফল্য বা ব্যর্থতার সংকেত দেওয়ার প্রতিশ্রুতি ফিরিয়ে দিতে পারে। reason প্যারামিটারে একটি DOMString রয়েছে যা বর্ণনা করে যে কেন স্ট্রিমটি বাতিল করা হয়েছিল।
const writableStream = new WritableStream({
  start(controller) {
    /* … */
  },

  write(chunk, controller) {
    /* … */
  },

  close(controller) {
    /* … */
  },

  abort(reason) {
    /* … */
  },
});

স্ট্রীমস API-এর WritableStreamDefaultController ইন্টারফেস একটি নিয়ামককে উপস্থাপন করে যা সেট আপের সময় একটি WritableStream এর অবস্থা নিয়ন্ত্রণ করতে দেয়, কারণ লেখার জন্য বা লেখার শেষে আরও বেশি অংশ জমা দেওয়া হয়। একটি WritableStream নির্মাণ করার সময়, অন্তর্নিহিত সিঙ্ককে ম্যানিপুলেট করার জন্য একটি সংশ্লিষ্ট WritableStreamDefaultController উদাহরণ দেওয়া হয়। WritableStreamDefaultController শুধুমাত্র একটি পদ্ধতি রয়েছে: WritableStreamDefaultController.error() , যা সংশ্লিষ্ট স্ট্রিমের সাথে ভবিষ্যতের কোনো ইন্টারঅ্যাকশনের ত্রুটি ঘটায়। WritableStreamDefaultController একটি signal সম্পত্তিকে সমর্থন করে যা AbortSignal এর একটি উদাহরণ প্রদান করে, প্রয়োজনে একটি WritableStream অপারেশন বন্ধ করার অনুমতি দেয়।

/* … */
write(chunk, controller) {
  try {
    // Try to do something dangerous with `chunk`.
  } catch (error) {
    controller.error(error.message);
  }
},
/* … */

queuingStrategy

দ্বিতীয়, একইভাবে ঐচ্ছিক, WritableStream() কনস্ট্রাক্টরের আর্গুমেন্ট হল queuingStrategy । এটি একটি বস্তু যা ঐচ্ছিকভাবে স্ট্রিমের জন্য একটি সারিবদ্ধ কৌশল নির্ধারণ করে, যা দুটি পরামিতি নেয়:

  • highWaterMark : একটি অ-নেতিবাচক সংখ্যা যা এই সারিবদ্ধ কৌশল ব্যবহার করে স্রোতের উচ্চ জলের চিহ্ন নির্দেশ করে।
  • size(chunk) : একটি ফাংশন যা গণনা করে এবং প্রদত্ত খণ্ড মানের সসীম অ-নেতিবাচক আকার প্রদান করে। ফলাফলটি ব্যাকপ্রেশার নির্ধারণ করতে ব্যবহৃত হয়, যথাযথ WritableStreamDefaultWriter.desiredSize সম্পত্তির মাধ্যমে প্রকাশ করে।

getWriter() এবং write() পদ্ধতি

একটি লিখনযোগ্য স্ট্রীমে লিখতে, আপনার একজন লেখকের প্রয়োজন, যেটি হবে একটি WritableStreamDefaultWriterWritableStream ইন্টারফেসের getWriter() পদ্ধতি WritableStreamDefaultWriter এর একটি নতুন উদাহরণ প্রদান করে এবং সেই দৃষ্টান্তে স্ট্রিমটিকে লক করে। স্ট্রীমটি লক থাকা অবস্থায়, বর্তমানটি প্রকাশিত না হওয়া পর্যন্ত অন্য কোন লেখককে অধিগ্রহণ করা যাবে না।

WritableStreamDefaultWriter ইন্টারফেসের write() পদ্ধতি একটি WritableStream এবং এর অন্তর্নিহিত সিঙ্কে ডেটার একটি অংশ লেখে, তারপর একটি প্রতিশ্রুতি প্রদান করে যা লেখার অপারেশনের সাফল্য বা ব্যর্থতা নির্দেশ করে। লক্ষ্য করুন যে "সাফল্য" এর অর্থ অন্তর্নিহিত সিঙ্ক পর্যন্ত; এটি ইঙ্গিত দিতে পারে যে খণ্ডটি গ্রহণ করা হয়েছে, এবং অগত্যা নয় যে এটি নিরাপদে তার চূড়ান্ত গন্তব্যে সংরক্ষণ করা হয়েছে।

const writer = writableStream.getWriter();
const resultPromise = writer.write('The first chunk!');

locked সম্পত্তি

WritableStream.locked প্রপার্টি অ্যাক্সেস করে আপনি একটি লিখনযোগ্য স্ট্রিম লক করা আছে কিনা তা পরীক্ষা করতে পারেন।

const locked = writableStream.locked;
console.log(`The stream is ${locked ? 'indeed' : 'not'} locked.`);

লেখার যোগ্য স্ট্রিম কোড নমুনা

নীচের কোড নমুনা কর্মের সমস্ত পদক্ষেপ দেখায়।

const writableStream = new WritableStream({
  start(controller) {
    console.log('[start]');
  },
  async write(chunk, controller) {
    console.log('[write]', chunk);
    // Wait for next write.
    await new Promise((resolve) => setTimeout(() => {
      document.body.textContent += chunk;
      resolve();
    }, 1_000));
  },
  close(controller) {
    console.log('[close]');
  },
  abort(reason) {
    console.log('[abort]', reason);
  },
});

const writer = writableStream.getWriter();
const start = Date.now();
for (const char of 'abcdefghijklmnopqrstuvwxyz') {
  // Wait to add to the write queue.
  await writer.ready;
  console.log('[ready]', Date.now() - start, 'ms');
  // The Promise is resolved after the write finishes.
  writer.write(char);
}
await writer.close();

একটি পঠনযোগ্য স্ট্রীমকে একটি লেখার যোগ্য স্ট্রীমে পাইপ করা হচ্ছে

একটি পঠনযোগ্য স্ট্রীম পঠনযোগ্য স্ট্রীমের pipeTo() পদ্ধতির মাধ্যমে একটি লিখনযোগ্য স্ট্রীমে পাইপ করা যেতে পারে। ReadableStream.pipeTo() একটি প্রদত্ত WritableStream এ বর্তমান ReadableStream পাইপ করে এবং একটি প্রতিশ্রুতি প্রদান করে যা পাইপিং প্রক্রিয়া সফলভাবে সম্পন্ন হলে পূরণ হয়, অথবা কোনো ত্রুটির সম্মুখীন হলে প্রত্যাখ্যান করে।

const readableStream = new ReadableStream({
  start(controller) {
    // Called by constructor.
    console.log('[start readable]');
    controller.enqueue('a');
    controller.enqueue('b');
    controller.enqueue('c');
  },
  pull(controller) {
    // Called when controller's queue is empty.
    console.log('[pull]');
    controller.enqueue('d');
    controller.close();
  },
  cancel(reason) {
    // Called when the stream is canceled.
    console.log('[cancel]', reason);
  },
});

const writableStream = new WritableStream({
  start(controller) {
    // Called by constructor
    console.log('[start writable]');
  },
  async write(chunk, controller) {
    // Called upon writer.write()
    console.log('[write]', chunk);
    // Wait for next write.
    await new Promise((resolve) => setTimeout(() => {
      document.body.textContent += chunk;
      resolve();
    }, 1_000));
  },
  close(controller) {
    console.log('[close]');
  },
  abort(reason) {
    console.log('[abort]', reason);
  },
});

await readableStream.pipeTo(writableStream);
console.log('[finished]');

একটি রূপান্তর প্রবাহ তৈরি করা হচ্ছে

স্ট্রীমস API-এর TransformStream ইন্টারফেস রূপান্তরযোগ্য ডেটার একটি সেট উপস্থাপন করে। আপনি এর কনস্ট্রাক্টর TransformStream() কে কল করে একটি ট্রান্সফর্ম স্ট্রীম তৈরি করেন, যা প্রদত্ত হ্যান্ডলার থেকে একটি ট্রান্সফর্ম স্ট্রিম অবজেক্ট তৈরি করে এবং ফেরত দেয়। TransformStream() কনস্ট্রাক্টর তার প্রথম আর্গুমেন্ট হিসেবে transformer প্রতিনিধিত্বকারী একটি ঐচ্ছিক জাভাস্ক্রিপ্ট অবজেক্ট গ্রহণ করে। এই ধরনের অবজেক্টে নিম্নলিখিত পদ্ধতিগুলির মধ্যে যেকোনো একটি থাকতে পারে:

transformer

  • start(controller) : অবজেক্টটি তৈরি হলেই এই পদ্ধতিটিকে অবিলম্বে বলা হয়। সাধারণত এটি controller.enqueue() ব্যবহার করে উপসর্গের অংশগুলি সারিবদ্ধ করতে ব্যবহৃত হয়। এই খণ্ডগুলি পঠনযোগ্য দিক থেকে পড়া হবে তবে লেখার যোগ্য দিক থেকে কোনও লেখার উপর নির্ভর করবে না। যদি এই প্রাথমিক প্রক্রিয়াটি অ্যাসিঙ্ক্রোনাস হয়, উদাহরণস্বরূপ কারণ এটি উপসর্গের অংশগুলি অর্জন করতে কিছু প্রচেষ্টা নেয়, ফাংশনটি সাফল্য বা ব্যর্থতার সংকেত দেওয়ার প্রতিশ্রুতি ফিরিয়ে দিতে পারে; একটি প্রত্যাখ্যান প্রতিশ্রুতি স্ট্রীম ত্রুটি হবে. TransformStream() কনস্ট্রাক্টর দ্বারা নিক্ষিপ্ত যেকোন ব্যতিক্রমগুলি পুনরায় নিক্ষেপ করা হবে।
  • transform(chunk, controller) : এই পদ্ধতিটিকে বলা হয় যখন একটি নতুন খণ্ড যা মূলত লেখার যোগ্য দিকে লিখিত রূপান্তরিত হওয়ার জন্য প্রস্তুত হয়। স্ট্রীম বাস্তবায়ন গ্যারান্টি দেয় যে এই ফাংশনটি শুধুমাত্র পূর্ববর্তী রূপান্তরগুলি সফল হওয়ার পরেই কল করা হবে, এবং start() সম্পূর্ণ হওয়ার আগে বা flush() কল করার আগে কখনও বলা হবে না। এই ফাংশনটি ট্রান্সফর্ম স্ট্রিমের প্রকৃত রূপান্তর কাজ সম্পাদন করে। এটি controller.enqueue() ব্যবহার করে ফলাফল সারিবদ্ধ করতে পারে। এটি একটি একক খণ্ডের অনুমতি দেয় যা লেখার যোগ্য পাশে শূন্য বা একাধিক খণ্ডে পরিণত হয়, যা controller.enqueue() কতবার কল করা হয়েছে তার উপর নির্ভর করে। যদি রূপান্তরের প্রক্রিয়াটি অ্যাসিঙ্ক্রোনাস হয়, তবে এই ফাংশনটি পরিবর্তনের সাফল্য বা ব্যর্থতার সংকেত দেওয়ার প্রতিশ্রুতি ফিরিয়ে দিতে পারে। একটি প্রত্যাখ্যান করা প্রতিশ্রুতি রূপান্তর স্ট্রিমের পঠনযোগ্য এবং লেখার যোগ্য উভয় দিকেই ত্রুটি করবে। যদি কোন transform() পদ্ধতি সরবরাহ করা না হয়, তাহলে পরিচয় ট্রান্সফর্ম ব্যবহার করা হয়, যা লেখার পাশ থেকে পঠনযোগ্য পাশ পর্যন্ত অপরিবর্তিত অংশগুলিকে সারিবদ্ধ করে।
  • flush(controller) : এই পদ্ধতিটিকে বলা হয় লিখনযোগ্য অংশে লেখা সমস্ত অংশ সফলভাবে transform() এর মধ্য দিয়ে পাস করে রূপান্তরিত হওয়ার পরে, এবং লেখার যোগ্য দিকটি বন্ধ হতে চলেছে। সাধারণত এটি পাঠযোগ্য দিকে প্রত্যয় অংশগুলিকে সারিবদ্ধ করতে ব্যবহৃত হয়, এটিও বন্ধ হওয়ার আগে। যদি ফ্লাশিং প্রক্রিয়াটি অ্যাসিঙ্ক্রোনাস হয়, তবে ফাংশনটি সাফল্য বা ব্যর্থতার সংকেত দেওয়ার প্রতিশ্রুতি ফিরিয়ে দিতে পারে; ফলাফল stream.writable.write() এর কলারকে জানানো হবে। অতিরিক্তভাবে, একটি প্রত্যাখ্যান করা প্রতিশ্রুতি স্ট্রিমের পঠনযোগ্য এবং লেখার যোগ্য উভয় দিকেই ত্রুটি করবে। একটি ব্যতিক্রম নিক্ষেপ একটি প্রত্যাখ্যান প্রতিশ্রুতি ফেরত হিসাবে একই বিবেচনা করা হয়.
const transformStream = new TransformStream({
  start(controller) {
    /* … */
  },

  transform(chunk, controller) {
    /* … */
  },

  flush(controller) {
    /* … */
  },
});

writableStrategy এবং readableStrategy সারিবদ্ধ কৌশল

TransformStream() কনস্ট্রাক্টরের দ্বিতীয় এবং তৃতীয় ঐচ্ছিক প্যারামিটার হল ঐচ্ছিক writableStrategy এবং readableStrategy সারিবদ্ধ কৌশল। এগুলি যথাক্রমে পঠনযোগ্য এবং লেখার যোগ্য স্ট্রিম বিভাগে রূপরেখা হিসাবে সংজ্ঞায়িত করা হয়েছে।

স্ট্রিম কোড নমুনা রূপান্তর

নিম্নলিখিত কোড নমুনা কর্মে একটি সাধারণ রূপান্তর স্ট্রীম দেখায়।

// Note that `TextEncoderStream` and `TextDecoderStream` exist now.
// This example shows how you would have done it before.
const textEncoderStream = new TransformStream({
  transform(chunk, controller) {
    console.log('[transform]', chunk);
    controller.enqueue(new TextEncoder().encode(chunk));
  },
  flush(controller) {
    console.log('[flush]');
    controller.terminate();
  },
});

(async () => {
  const readStream = textEncoderStream.readable;
  const writeStream = textEncoderStream.writable;

  const writer = writeStream.getWriter();
  for (const char of 'abc') {
    writer.write(char);
  }
  writer.close();

  const reader = readStream.getReader();
  for (let result = await reader.read(); !result.done; result = await reader.read()) {
    console.log('[value]', result.value);
  }
})();

ট্রান্সফর্ম স্ট্রিমের মাধ্যমে একটি পঠনযোগ্য স্ট্রীম পাইপ করা

ReadableStream ইন্টারফেসের pipeThrough() পদ্ধতি একটি ট্রান্সফর্ম স্ট্রীম বা অন্য কোন লিখনযোগ্য/পঠনযোগ্য জোড়ার মাধ্যমে বর্তমান স্ট্রীমকে পাইপ করার একটি চেইনযোগ্য উপায় প্রদান করে। একটি স্ট্রীম পাইপ করা সাধারণত এটিকে পাইপের সময়কালের জন্য লক করে দেয়, অন্য পাঠকদের এটি লক করা থেকে বাধা দেয়।

const transformStream = new TransformStream({
  transform(chunk, controller) {
    console.log('[transform]', chunk);
    controller.enqueue(new TextEncoder().encode(chunk));
  },
  flush(controller) {
    console.log('[flush]');
    controller.terminate();
  },
});

const readableStream = new ReadableStream({
  start(controller) {
    // called by constructor
    console.log('[start]');
    controller.enqueue('a');
    controller.enqueue('b');
    controller.enqueue('c');
  },
  pull(controller) {
    // called read when controller's queue is empty
    console.log('[pull]');
    controller.enqueue('d');
    controller.close(); // or controller.error();
  },
  cancel(reason) {
    // called when rs.cancel(reason)
    console.log('[cancel]', reason);
  },
});

(async () => {
  const reader = readableStream.pipeThrough(transformStream).getReader();
  for (let result = await reader.read(); !result.done; result = await reader.read()) {
    console.log('[value]', result.value);
  }
})();

পরবর্তী কোডের নমুনা (একটু বানোয়াট) দেখায় কিভাবে আপনি fetch() এর একটি "চিৎকার" সংস্করণ বাস্তবায়ন করতে পারেন যা প্রত্যাবর্তিত প্রতিক্রিয়া প্রতিশ্রুতিটিকে একটি স্ট্রীম হিসাবে গ্রহণ করে এবং খণ্ডে বড় হাতের অংশ হিসাবে ব্যবহার করে সমস্ত পাঠকে বড় করে। এই পদ্ধতির সুবিধা হল যে আপনাকে সম্পূর্ণ নথি ডাউনলোড করার জন্য অপেক্ষা করতে হবে না, যা বড় ফাইলগুলির সাথে কাজ করার সময় একটি বিশাল পার্থক্য করতে পারে।

function upperCaseStream() {
  return new TransformStream({
    transform(chunk, controller) {
      controller.enqueue(chunk.toUpperCase());
    },
  });
}

function appendToDOMStream(el) {
  return new WritableStream({
    write(chunk) {
      el.append(chunk);
    }
  });
}

fetch('./lorem-ipsum.txt').then((response) =>
  response.body
    .pipeThrough(new TextDecoderStream())
    .pipeThrough(upperCaseStream())
    .pipeTo(appendToDOMStream(document.body))
);

ডেমো

নীচের ডেমোটি পঠনযোগ্য, লেখার যোগ্য, এবং কর্মে স্ট্রিম রূপান্তর দেখায়। এটি pipeThrough() এবং pipeTo() পাইপ চেইনের উদাহরণও অন্তর্ভুক্ত করে এবং tee() প্রদর্শন করে। আপনি ঐচ্ছিকভাবে তার নিজস্ব উইন্ডোতে ডেমো চালাতে পারেন বা সোর্স কোড দেখতে পারেন।

ব্রাউজারে উপলভ্য দরকারী স্ট্রীম

ব্রাউজারে বেশ কয়েকটি দরকারী স্ট্রীম তৈরি করা হয়েছে। আপনি সহজেই একটি ব্লব থেকে একটি ReadableStream তৈরি করতে পারেন। Blob ইন্টারফেসের স্ট্রিম() পদ্ধতি একটি ReadableStream প্রদান করে যা পড়ার পরে ব্লবের মধ্যে থাকা ডেটা ফেরত দেয়। এছাড়াও মনে রাখবেন যে একটি File অবজেক্ট হল একটি নির্দিষ্ট ধরণের একটি Blob , এবং যে কোনও প্রসঙ্গে ব্যবহার করা যেতে পারে যা একটি ব্লব করতে পারে।

const readableStream = new Blob(['hello world'], { type: 'text/plain' }).stream();

TextDecoder.decode() এবং TextEncoder.encode() এর স্ট্রিমিং ভেরিয়েন্টগুলিকে যথাক্রমে TextDecoderStream এবং TextEncoderStream বলা হয়।

const response = await fetch('https://streams.spec.whatwg.org/');
const decodedStream = response.body.pipeThrough(new TextDecoderStream());

CompressionStream এবং DecompressionStream স্ট্রিম ট্রান্সফর্ম স্ট্রীমগুলির সাথে যথাক্রমে একটি ফাইল কম্প্রেস বা ডিকম্প্রেস করা সহজ। নীচের কোড নমুনাটি দেখায় যে আপনি কীভাবে স্ট্রিম স্পেক ডাউনলোড করতে পারেন, ব্রাউজারে এটিকে সংকুচিত করতে পারেন (জিজিপ), এবং সংকুচিত ফাইলটি সরাসরি ডিস্কে লিখতে পারেন।

const response = await fetch('https://streams.spec.whatwg.org/');
const readableStream = response.body;
const compressedStream = readableStream.pipeThrough(new CompressionStream('gzip'));

const fileHandle = await showSaveFilePicker();
const writableStream = await fileHandle.createWritable();
compressedStream.pipeTo(writableStream);

ফাইল সিস্টেম অ্যাক্সেস API- এর FileSystemWritableFileStream এবং পরীক্ষামূলক fetch() অনুরোধ স্ট্রীমগুলি বন্যের লেখার যোগ্য স্ট্রিমগুলির উদাহরণ।

সিরিয়াল এপিআই পঠনযোগ্য এবং লেখার যোগ্য উভয় স্ট্রিমের ভারী ব্যবহার করে।

// Prompt user to select any serial port.
const port = await navigator.serial.requestPort();
// Wait for the serial port to open.
await port.open({ baudRate: 9_600 });
const reader = port.readable.getReader();

// Listen to data coming from the serial device.
while (true) {
  const { value, done } = await reader.read();
  if (done) {
    // Allow the serial port to be closed later.
    reader.releaseLock();
    break;
  }
  // value is a Uint8Array.
  console.log(value);
}

// Write to the serial port.
const writer = port.writable.getWriter();
const data = new Uint8Array([104, 101, 108, 108, 111]); // hello
await writer.write(data);
// Allow the serial port to be closed later.
writer.releaseLock();

অবশেষে, WebSocketStream API WebSocket API-এর সাথে স্ট্রিমগুলিকে একীভূত করে।

const wss = new WebSocketStream(WSS_URL);
const { readable, writable } = await wss.connection;
const reader = readable.getReader();
const writer = writable.getWriter();

while (true) {
  const { value, done } = await reader.read();
  if (done) {
    break;
  }
  const result = await process(value);
  await writer.write(result);
}

দরকারী সম্পদ

স্বীকৃতি

এই নিবন্ধটি জেক আর্চিবল্ড , ফ্রাঙ্কোইস বিউফোর্ট , স্যাম ডাটন , ম্যাটিয়াস বুয়েলেনস , সুরমা , জো মেডলি , এবং অ্যাডাম রাইস দ্বারা পর্যালোচনা করা হয়েছে। Jake Archibald এর ব্লগ পোস্টগুলি আমাকে স্ট্রীম বুঝতে অনেক সাহায্য করেছে। কিছু কোড নমুনা GitHub ব্যবহারকারী @bellbind- এর অন্বেষণ এবং গদ্যের অংশগুলি স্ট্রীমসের MDN ওয়েব ডক্সে ব্যাপকভাবে তৈরি করা থেকে অনুপ্রাণিত। স্ট্রীমস স্ট্যান্ডার্ডের লেখকরা এই বিশেষত্বটি লেখার জন্য একটি অসাধারণ কাজ করেছেন। আনস্প্ল্যাশে রায়ান লারার হিরো ছবি।