سؤال

هل هناك طريقة ما للقيام بسلاسل المحادثات المتعددة في JavaScript؟

هل كانت مفيدة؟

المحلول

يرى http://caniuse.com/#search=worker للحصول على أحدث معلومات الدعم.

فيما يلي حالة الدعم حوالي عام 2009.


الكلمات التي تريد البحث عنها في جوجل هي خيوط عامل جافا سكريبت

بصرف النظر عن التروس لا يوجد شيء متاح في الوقت الحالي، ولكن هناك الكثير من الحديث حول كيفية تنفيذ ذلك، لذا أعتقد أن عليك مراقبة هذا السؤال لأن الإجابة ستتغير بلا شك في المستقبل.

إليك الوثائق ذات الصلة بـ Gears: واجهة برمجة تطبيقات WorkerPool

لدى WHATWG مسودة توصية لسلاسل العمليات العاملة: عمال الويب

وهناك أيضًا موزيلا خيوط عامل DOM


تحديث: يونيو 2009، الوضع الحالي لدعم المتصفح لسلاسل JavaScript

فايرفوكس 3.5 لديه عمال على شبكة الإنترنت.بعض العروض التوضيحية لعمال الويب، إذا كنت تريد رؤيتها أثناء العمل:

يمكن أيضًا تثبيت المكون الإضافي Gears في Firefox.

سفاري 4, ، و ال صحف WebKit الليلية لديك مؤشرات الترابط العاملة:

كروم يحتوي على Gears مدمجًا، لذا يمكنه تنفيذ سلاسل الرسائل، على الرغم من أنه يتطلب تأكيدًا من المستخدم (ويستخدم واجهة برمجة تطبيقات مختلفة للعاملين على الويب، على الرغم من أنه سيعمل في أي متصفح مثبت عليه المكون الإضافي لـ Gears):

  • العرض التجريبي لـ Google Gears WorkerPool (ليس مثالًا جيدًا لأنه يعمل بسرعة كبيرة جدًا بحيث لا يمكن اختباره في Chrome وFirefox، على الرغم من أن IE يعمل عليه ببطء كافٍ لرؤيته يحظر التفاعل)

IE8 و IE9 لا يمكن تنفيذ سلاسل الرسائل إلا مع تثبيت البرنامج الإضافي Gears

نصائح أخرى

طريقة مختلفة للقيام بخيوط متعددة وغير متزامنة في JavaScript

قبل HTML5، كانت JavaScript تسمح فقط بتنفيذ موضوع واحد لكل صفحة.

كانت هناك طريقة ماكرة لمحاكاة التنفيذ غير المتزامن باستخدام أَثْمَر, setTimeout(), setInterval(), XMLHttpRequest أو معالجات الأحداث (انظر نهاية هذا المنشور للحصول على مثال مع أَثْمَر و setTimeout()).

ولكن مع HTML5 يمكننا الآن استخدام مؤشرات الترابط العاملة لموازاة تنفيذ الوظائف.هنا مثال على الاستخدام.


خيوط متعددة حقيقية

متعدد الخيوط:خيوط عامل جافا سكريبت

HTML5 قدمت خيوط عامل الويب (انظر: توافق المتصفحات)
ملحوظة:IE9 والإصدارات السابقة لا تدعمه.

سلاسل العمليات هذه هي سلاسل رسائل JavaScript يتم تشغيلها في الخلفية دون التأثير على أداء الصفحة.لمزيد من المعلومات حول عامل الويب قراءة الوثائق أو هذا البرنامج التعليمي.

فيما يلي مثال بسيط يحتوي على 3 سلاسل رسائل Web Worker التي يصل عددها إلى MAX_VALUE وتظهر القيمة المحسوبة الحالية في صفحتنا:

//As a worker normally take another JavaScript file to execute we convert the function in an URL: http://stackoverflow.com/a/16799132/2576706
function getScriptPath(foo){ return window.URL.createObjectURL(new Blob([foo.toString().match(/^\s*function\s*\(\s*\)\s*\{(([\s\S](?!\}$))*[\s\S])/)[1]],{type:'text/javascript'})); }

var MAX_VALUE = 10000;

/*
 *	Here are the workers
 */
//Worker 1
var worker1 = new Worker(getScriptPath(function(){
    self.addEventListener('message', function(e) {
        var value = 0;
        while(value <= e.data){
            self.postMessage(value);
            value++;
        }
    }, false);
}));
//We add a listener to the worker to get the response and show it in the page
worker1.addEventListener('message', function(e) {
  document.getElementById("result1").innerHTML = e.data;
}, false);


//Worker 2
var worker2 = new Worker(getScriptPath(function(){
    self.addEventListener('message', function(e) {
        var value = 0;
        while(value <= e.data){
            self.postMessage(value);
            value++;
        }
    }, false);
}));
worker2.addEventListener('message', function(e) {
  document.getElementById("result2").innerHTML = e.data;
}, false);


//Worker 3
var worker3 = new Worker(getScriptPath(function(){
    self.addEventListener('message', function(e) {
        var value = 0;
        while(value <= e.data){
            self.postMessage(value);
            value++;
        }
    }, false);
}));
worker3.addEventListener('message', function(e) {
    document.getElementById("result3").innerHTML = e.data;
}, false);


// Start and send data to our worker.
worker1.postMessage(MAX_VALUE); 
worker2.postMessage(MAX_VALUE); 
worker3.postMessage(MAX_VALUE);
<div id="result1"></div>
<div id="result2"></div>
<div id="result3"></div>

يمكننا أن نرى أن المواضيع الثلاثة يتم تنفيذها بشكل متزامن وطباعة قيمتها الحالية في الصفحة.لا يقومون بتجميد الصفحة لأنه يتم تنفيذها في الخلفية باستخدام سلاسل رسائل منفصلة.


متعدد الخيوط:مع إطارات iframe متعددة

هناك طريقة أخرى لتحقيق ذلك وهي استخدام عدة إطارات iframe, ، سيقوم كل واحد بتنفيذ موضوع.يمكننا أن نعطي com.iframe بعض المعلمات بواسطة عنوان URL و com.iframe يمكنه التواصل مع ولي أمره للحصول على النتيجة وطباعتها مرة أخرى (الملف com.iframe يجب أن يكون في نفس المجال).

هذا المثال لا يعمل في جميع المتصفحات! إطارات iframe يتم تشغيله عادةً في نفس الموضوع/العملية مثل الصفحة الرئيسية (ولكن يبدو أن Firefox وChromium يتعاملان معها بشكل مختلف).

وبما أن مقتطف الشفرة لا يدعم ملفات HTML متعددة، فسوف أقوم فقط بتوفير الرموز المختلفة هنا:

الفهرس.html:

//The 3 iframes containing the code (take the thread id in param)
<iframe id="threadFrame1" src="thread.html?id=1"></iframe>
<iframe id="threadFrame2" src="thread.html?id=2"></iframe>
<iframe id="threadFrame3" src="thread.html?id=3"></iframe>

//Divs that shows the result
<div id="result1"></div>
<div id="result2"></div>
<div id="result3"></div>


<script>
    //This function is called by each iframe
    function threadResult(threadId, result) {
        document.getElementById("result" + threadId).innerHTML = result;
    }
</script>

الموضوع.html:

//Get the parameters in the URL: http://stackoverflow.com/a/1099670/2576706
function getQueryParams(paramName) {
    var qs = document.location.search.split('+').join(' ');
    var params = {}, tokens, re = /[?&]?([^=]+)=([^&]*)/g;
    while (tokens = re.exec(qs)) {
        params[decodeURIComponent(tokens[1])] = decodeURIComponent(tokens[2]);
    }
    return params[paramName];
}

//The thread code (get the id from the URL, we can pass other parameters as needed)
var MAX_VALUE = 100000;
(function thread() {
    var threadId = getQueryParams('id');
    for(var i=0; i<MAX_VALUE; i++){
        parent.threadResult(threadId, i);
    }
})();

محاكاة خيوط متعددة

موضوع واحد:محاكاة التزامن JavaScript مع setTimeout()

الطريقة "الساذجة" هي تنفيذ الوظيفة setTimeout() واحدا تلو الآخر مثل هذا:

setTimeout(function(){ /* Some tasks */ }, 0);
setTimeout(function(){ /* Some tasks */ }, 0);
[...]

ولكن هذا الأسلوب لا يعمل لأنه سيتم تنفيذ كل مهمة واحدة تلو الأخرى.

يمكننا محاكاة التنفيذ غير المتزامن عن طريق استدعاء الدالة بشكل متكرر مثل هذا:

var MAX_VALUE = 10000;

function thread1(value, maxValue){
    var me = this;
    document.getElementById("result1").innerHTML = value;
    value++;
  
    //Continue execution
    if(value<=maxValue)
        setTimeout(function () { me.thread1(value, maxValue); }, 0);
}

function thread2(value, maxValue){
    var me = this;
    document.getElementById("result2").innerHTML = value;
    value++;
	
    if(value<=maxValue)
        setTimeout(function () { me.thread2(value, maxValue); }, 0);
}

function thread3(value, maxValue){
    var me = this;
    document.getElementById("result3").innerHTML = value;
    value++;
	
    if(value<=maxValue)
        setTimeout(function () { me.thread3(value, maxValue); }, 0);
}

thread1(0, MAX_VALUE);
thread2(0, MAX_VALUE);
thread3(0, MAX_VALUE);
<div id="result1"></div>
<div id="result2"></div>
<div id="result3"></div>

كما ترون، هذه الطريقة الثانية بطيئة جدًا وتجمد المتصفح لأنها تستخدم الخيط الرئيسي لتنفيذ الوظائف.


موضوع واحد:محاكاة تزامن جافا سكريبت مع العائد

أَثْمَر هي ميزة جديدة في إيكماسكريبت 6, ، فهو يعمل فقط على الإصدار الأقدم من Firefox وChrome (في Chrome تحتاج إلى تمكين جافا سكريبت التجريبية تظهر في chrome://flags/#enable-javascript-harmony).

تؤدي الكلمة الأساسية الخاصة بالعائد إلى إيقاف تنفيذ وظيفة المولد مؤقتًا ويتم إرجاع قيمة التعبير الذي يتبع الكلمة الأساسية الخاصة بالعائد إلى المتصل بالمولد.يمكن اعتبارها نسخة قائمة على المولد للكلمة الأساسية return.

يتيح لك المولد تعليق تنفيذ الوظيفة واستئنافها لاحقًا.يمكن استخدام المولد لجدولة وظائفك باستخدام تقنية تسمى الترامبولين.

هنا هو المثال:

var MAX_VALUE = 10000;

Scheduler = {
	_tasks: [],
	add: function(func){
		this._tasks.push(func);
	},	
	start: function(){
		var tasks = this._tasks;
		var length = tasks.length;
		while(length>0){
			for(var i=0; i<length; i++){
				var res = tasks[i].next();
				if(res.done){
					tasks.splice(i, 1);
					length--;
					i--;
				}
			}
		}
	}	
}


function* updateUI(threadID, maxValue) {
  var value = 0;
  while(value<=maxValue){
	yield document.getElementById("result" + threadID).innerHTML = value;
	value++;
  }
}

Scheduler.add(updateUI(1, MAX_VALUE));
Scheduler.add(updateUI(2, MAX_VALUE));
Scheduler.add(updateUI(3, MAX_VALUE));

Scheduler.start()
<div id="result1"></div>
<div id="result2"></div>
<div id="result3"></div>

مع "المواصفات الجانبية" لـ HTML5 لا حاجة لاختراق جافا سكريبت بعد الآن باستخدام setTimeout() وsetInterval() وما إلى ذلك.

HTML5 & Friends يقدم جافا سكريبت عمال الويب تخصيص.إنها واجهة برمجة التطبيقات (API) لتشغيل البرامج النصية بشكل غير متزامن ومستقل.

روابط إلى تخصيص و أ درس تعليمي.

لا يوجد ترابط حقيقي في JavaScript.نظرًا لأن JavaScript هي اللغة المرنة، فهي تسمح لك بمحاكاة بعض منها.هنا مثال جئت عبر في اليوم الآخر.

لا يوجد تعدد حقيقي في جافا سكريبت، ولكن يمكنك الحصول على سلوك غير متزامن باستخدام setTimeout() وطلبات AJAX غير المتزامنة.

ما الذي تحاول تنفيذه تحديدا؟

هنا مجرد وسيلة ل محاكاة تعدد الخيوط في جافا سكريبت

الآن سأقوم بإنشاء 3 مواضيع والتي سوف تحسب جمع الأرقام، يمكن تقسيم الأرقام على 13 ويمكن تقسيم الأرقام على 3 حتى 10000000000.وهذه الوظائف الثلاث غير قادرة على العمل في نفس الوقت الذي يعنيه التزامن.لكنني سأعرض لك خدعة تجعل هذه الوظائف تعمل بشكل متكرر في نفس الوقت: jsFiddle

هذا الرمز ينتمي لي.

جزء من الجسم

    <div class="div1">
    <input type="button" value="start/stop" onclick="_thread1.control ? _thread1.stop() : _thread1.start();" /><span>Counting summation of numbers till 10000000000</span> = <span id="1">0</span>
</div>
<div class="div2">
    <input type="button" value="start/stop" onclick="_thread2.control ? _thread2.stop() : _thread2.start();" /><span>Counting numbers can be divided with 13 till 10000000000</span> = <span id="2">0</span>
</div>
<div class="div3">
    <input type="button" value="start/stop" onclick="_thread3.control ? _thread3.stop() : _thread3.start();" /><span>Counting numbers can be divided with 3 till 10000000000</span> = <span id="3">0</span>
</div>

جافا سكريبت الجزء

var _thread1 = {//This is my thread as object
    control: false,//this is my control that will be used for start stop
    value: 0, //stores my result
    current: 0, //stores current number
    func: function () {   //this is my func that will run
        if (this.control) {      // checking for control to run
            if (this.current < 10000000000) {
                this.value += this.current;   
                document.getElementById("1").innerHTML = this.value;
                this.current++;
            }
        }
        setTimeout(function () {  // And here is the trick! setTimeout is a king that will help us simulate threading in javascript
            _thread1.func();    //You cannot use this.func() just try to call with your object name
        }, 0);
    },
    start: function () {
        this.control = true;   //start function
    },
    stop: function () {
        this.control = false;    //stop function
    },
    init: function () {
        setTimeout(function () {
            _thread1.func();    // the first call of our thread
        }, 0)
    }
};
var _thread2 = {
    control: false,
    value: 0,
    current: 0,
    func: function () {
        if (this.control) {
            if (this.current % 13 == 0) {
                this.value++;
            }
            this.current++;
            document.getElementById("2").innerHTML = this.value;
        }
        setTimeout(function () {
            _thread2.func();
        }, 0);
    },
    start: function () {
        this.control = true;
    },
    stop: function () {
        this.control = false;
    },
    init: function () {
        setTimeout(function () {
            _thread2.func();
        }, 0)
    }
};
var _thread3 = {
    control: false,
    value: 0,
    current: 0,
    func: function () {
        if (this.control) {
            if (this.current % 3 == 0) {
                this.value++;
            }
            this.current++;
            document.getElementById("3").innerHTML = this.value;
        }
        setTimeout(function () {
            _thread3.func();
        }, 0);
    },
    start: function () {
        this.control = true;
    },
    stop: function () {
        this.control = false;
    },
    init: function () {
        setTimeout(function () {
            _thread3.func();
        }, 0)
    }
};

_thread1.init();
_thread2.init();
_thread3.init();

آمل أن تكون هذه الطريقة مفيدة.

يمكنك استخدام جافا سكريبت السرد, ، وهو مترجم يقوم بتحويل التعليمات البرمجية الخاصة بك إلى آلة حالة، مما يسمح لك بمحاكاة الترابط بشكل فعال.ويتم ذلك عن طريق إضافة عامل تشغيل "يخضع" (يُشار إليه بالرمز '->') إلى اللغة التي تسمح لك بكتابة تعليمات برمجية غير متزامنة في كتلة تعليمات برمجية خطية واحدة.

محرك V8 الجديد الذي يجب أن يخرج اليوم يدعمها (على ما أعتقد)

هناك طريقة أخرى ممكنة وهي استخدام مترجم جافا سكريبت في بيئة جافا سكريبت.

من خلال إنشاء مترجمين فوريين متعددين والتحكم في تنفيذهم من سلسلة المحادثات الرئيسية، يمكنك محاكاة مؤشرات الترابط المتعددة مع تشغيل كل سلسلة رسائل في بيئتها الخاصة.

يشبه هذا الأسلوب إلى حد ما العاملين على الويب، لكنك تمنح المترجم إمكانية الوصول إلى البيئة العالمية للمتصفح.

لقد قمت بعمل مشروع صغير ل إثبات هذا.

شرح أكثر تفصيلا في هذا بلوق وظيفة.

في جافا سكريبت الخام، أفضل ما يمكنك القيام به هو استخدام عدد قليل من الاستدعاءات غير المتزامنة (xmlhttprequest)، ولكن هذا ليس ترابطًا حقيقيًا ومحدودًا للغاية. جوجل جيرز يضيف عددًا من واجهات برمجة التطبيقات (APIs) إلى المتصفح، والتي يمكن استخدام بعضها لدعم الخيوط.

إذا كنت لا تستطيع أو لا تريد استخدام أي من عناصر AJAX، فاستخدم iframe أو عشرة!؛) يمكن أن يكون لديك عمليات تعمل في إطارات iframe بالتوازي مع الصفحة الرئيسية دون القلق بشأن المشكلات القابلة للمقارنة عبر المتصفحات أو مشكلات بناء الجملة مع dot net AJAX وما إلى ذلك، ويمكنك استدعاء JavaScript للصفحة الرئيسية (بما في ذلك JavaScript التي تم استيرادها) من com.iframe.

على سبيل المثال، في iframe الأصل، للاتصال egFunction() في المستند الأصلي بمجرد تحميل محتوى iframe (هذا هو الجزء غير المتزامن)

parent.egFunction();

قم بإنشاء إطارات iframe ديناميكيًا أيضًا بحيث يكون كود html الرئيسي خاليًا منها إذا كنت تريد ذلك.

جافا سكريبت لا تحتوي على سلاسل رسائل، ولكن لدينا عمال.

قد يكون العاملون خيارًا جيدًا إذا لم تكن بحاجة إلى كائنات مشتركة.

ستعمل معظم تطبيقات المتصفح على توزيع العمال عبر جميع النوى مما يسمح لك باستخدام جميع النوى.يمكنك رؤية عرض توضيحي لهذا هنا.

لقد قمت بتطوير مكتبة تسمى مهمة.js وهذا يجعل من السهل جدًا القيام بذلك.

مهمة.js واجهة مبسطة للحصول على تعليمات برمجية مكثفة لوحدة المعالجة المركزية (CPU) لتشغيلها على جميع النوى (node.js والويب)

على سبيل المثال سيكون

function blocking (exampleArgument) {
    // block thread
}

// turn blocking pure function into a worker task
const blockingAsync = task.wrap(blocking);

// run task on a autoscaling worker pool
blockingAsync('exampleArgumentValue').then(result => {
    // do something with result
});

مع HTML5 تخصيص لا تحتاج إلى كتابة الكثير من JS لنفسه أو العثور على بعض الاختراقات.

إحدى الميزات المقدمة في HTML5 هي عمال الويب وهو جافا سكريبت الذي يعمل في الخلفية، بشكل مستقل عن البرامج النصية الأخرى، دون التأثير على أداء الصفحة.

وهو مدعوم في جميع المتصفحات تقريبًا:

كروم - 4.0+

آي إي - 10.0+

موزيلا - 3.5+

سفاري - 4.0+

الأوبرا - 11.5+

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top