انتقل إلى المحتوى

التدفقات

ينفذ Phasis معيار تدفقات WHATWG كاملًا. كل فئة وكل نمط قارئ وكل مساعد. تعمل الشيفرة نفسها التي تعمل في Node والمتصفحات الحديثة في Phasis.

التنفيذ هو ما يستخدمه fetch داخليًا — فـResponse.body هو ReadableStream حقيقي، ويقبل Request أجسام الاستجابة المتدفقة، وتعمل tee() اللازمة لاستنساخ Response.

ما تم شحنه

الفئة الحالة
ReadableStream كاملة
ReadableStreamDefaultController كاملة
ReadableByteStreamController كاملة
ReadableStreamDefaultReader كاملة
ReadableStreamBYOBReader كاملة
ReadableStreamBYOBRequest كاملة
WritableStream كاملة
WritableStreamDefaultController كاملة
WritableStreamDefaultWriter كاملة
TransformStream كاملة
TransformStreamDefaultController كاملة
ByteLengthQueuingStrategy كاملة
CountQueuingStrategy كاملة

إضافةً إلى الأساليب التي تحتاج إليها: pipeTo، pipeThrough، tee، getReader، والتكرار غير المتزامن، وSymbol.asyncIterator، وقراءة BYOB باستخدام { min }.

الإنتاج

JavaScript
// Pull-based — only generates values when read
const counter = new ReadableStream({
  start(controller) { this.n = 0; },
  pull(controller) {
    if (this.n >= 5) { controller.close(); return; }
    controller.enqueue(this.n++);
  },
});

for await (const v of counter) console.log(v);  // 0,1,2,3,4
JavaScript
// Push-based — start enqueues everything eagerly
const small = new ReadableStream({
  start(c) {
    [10, 20, 30].forEach(n => c.enqueue(n));
    c.close();
  },
});

الاستهلاك

ثلاث طرق للقراءة:

JavaScript
// 1. Reader
const reader = stream.getReader();
while (true) {
  const { value, done } = await reader.read();
  if (done) break;
  console.log(value);
}
reader.releaseLock();

// 2. Async iteration (preferred)
for await (const chunk of stream) {
  console.log(chunk);
}

// 3. tee + parallel pipes
const [a, b] = stream.tee();
await Promise.all([a.pipeTo(sinkA), b.pipeTo(sinkB)]);

التحويل

JavaScript
const upper = new TransformStream({
  transform(chunk, controller) {
    controller.enqueue(chunk.toUpperCase());
  },
});

const result = new ReadableStream({
  start(c) { ['hi', 'there'].forEach(s => c.enqueue(s)); c.close(); }
}).pipeThrough(upper);

for await (const v of result) console.log(v);  // HI, THERE

تدفقات البايتات (BYOB)

JavaScript
const bytes = new ReadableStream({
  type: 'bytes',
  start(controller) {
    controller.enqueue(new Uint8Array([1, 2, 3, 4, 5, 6]));
    controller.close();
  },
});

const reader = bytes.getReader({ mode: 'byob' });
const buf = new Uint8Array(4);
const { value, done } = await reader.read(buf);
// value is a Uint8Array of length 4, sharing buffer with `buf`

{ min: N } مدعوم — لا يُحلّ read(view, { min: 2 }) إلا عند توفر عنصرين على الأقل.

الضغط العكسي

يعمل desiredSize وready كما يصف المعيار. استخدم استراتيجية اصطفاف لمنح المنتج قناةً عكسية:

JavaScript
const stream = new ReadableStream({
  pull(controller) {
    if (controller.desiredSize <= 0) return;  // back off
    controller.enqueue(nextChunk());
  },
}, new CountQueuingStrategy({ highWaterMark: 4 }));

مع fetch

JavaScript
// Response body is a ReadableStream
const res = await fetch('https://example.com/big.txt');
let total = 0;
for await (const chunk of res.body) {
  total += chunk.byteLength;
}

// Request bodies can stream too
const upload = new ReadableStream({
  start(c) { c.enqueue(new TextEncoder().encode('hello')); c.close(); }
});
await fetch('https://example.com/upload', { method: 'POST', body: upload });

Tee للتخزين المؤقت

نمط شائع: قراءة المتن مرتين.

JavaScript
const res = await fetch(url);
const [forCache, forUse] = res.body.tee();
await Promise.all([
  forCache.pipeTo(cacheWritable),
  parseJson(forUse),
]);

يتولى Response.clone() ذلك ويعيد Response جديدًا.

ملاحظات

  • المنتِجات المتزامنة فقط. سيؤدي pull() الذي ينفذ عمليات إدخال وإخراج حاجبة عبر PHP إلى حجب البرنامج النصي. استخدم خطاف نقل fetch من أجل HTTP؛ وبالنسبة إلى الملفات، يُفضّل قراءتها كاملةً إلى الذاكرة مسبقًا. يتوفر setTimeout / setInterval (انظر حلقة الأحداث)، لكن المحرك يستخدم وقتًا افتراضيًا افتراضيًا — فـsetTimeout(cb, 1000) داخل pull() لا ينتظر PHP، بل يقدّم ساعة الحلقة. يتطلب ضبط الإيقاع في الوقت الفعلي أن يقود المضيف tickEventLoop() وفق ساعة فعلية.

راجع أيضًا

  • واجهات Web API (WPT) — جدول معدل اجتياز WPT الذي يغطي الاختبارات الفرعية للتدفقات.
  • نقل Fetch — كيفية عبور متني الطلب والاستجابة للحد الفاصل بين PHP وJS.
  • أنماط AbortController — إلغاء عمليات القراءة الجارية.