90 votes

Comment créer un Web Worker à partir d'une chaîne de caractères ?

Comment puis-je utiliser la création d'un Web worker à partir d'une chaîne de caractères (fournie par une requête POST) ?

Un moyen auquel je pense, mais je ne suis pas sûr de la façon de l'implémenter, est de créer un data-URI à partir de la réponse du serveur, et de le passer au constructeur du Worker, mais j'ai entendu dire que certains navigateurs ne l'autorisent pas, à cause de la politique de la même origine.

MDN fait part de son incertitude quant à la politique d'origine des URI de données. :

Note : L'URI passé en paramètre du constructeur du Worker doit obéir à la politique de same-origin. Il existe actuellement un désaccord entre les fournisseurs de navigateurs sur la question de savoir si les URI de données sont de même origine ou non ; Gecko 10.0 (Firefox 10.0 / Thunderbird 10.0) et les versions ultérieures autorisent les URI de données comme script valide pour les travailleurs. D'autres navigateurs peuvent ne pas être d'accord.

Voici également un post en discuter sur le whatwg .

0 votes

Je me demande si CORS ( w3.org/TR/cors ) serait utile. HTMl5rocks utilise un langage fort "doit" lorsqu'il s'agit de la politique de même origine pour les travailleurs ( html5rocks.com/fr/tutoriels/travailleurs/éléments de base ) donc peut-être que CORS n'est pas d'une grande aide ici. L'avez-vous essayé ?

152voto

Rob W Points 125904

Résumé

  • blob: pour Chrome 8+, Firefox 6+, Safari 6.0+, Opera 15+.
  • data:application/javascript pour Opera 10.60 - 12
  • eval sinon (IE 10+)

URL.createObjectURL(<Blob blob>) peut être utilisé pour créer un travailleur Web à partir d'une chaîne de caractères. Le blob peut être créé à l'aide de la fonction BlobBuilder API déprécié ou le Blob Constructeur .

Démonstration : http://jsfiddle.net/uqcFM/49/

// URL.createObjectURL
window.URL = window.URL || window.webkitURL;

// "Server response", used in all examples
var response = "self.onmessage=function(e){postMessage('Worker: '+e.data);}";

var blob;
try {
    blob = new Blob([response], {type: 'application/javascript'});
} catch (e) { // Backwards-compatibility
    window.BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder;
    blob = new BlobBuilder();
    blob.append(response);
    blob = blob.getBlob();
}
var worker = new Worker(URL.createObjectURL(blob));

// Test, used in all examples:
worker.onmessage = function(e) {
    alert('Response: ' + e.data);
};
worker.postMessage('Test');

Compatibilité

Les travailleurs Web sont pris en charge par les navigateurs suivants source :

  • Chrome 3
  • Firefox 3.5
  • IE 10
  • Opera 10.60
  • Safari 4

Le support de cette méthode est basé sur le support de l'option Blob et l'API URL.createObjectUrl méthode. Blob compatibilité :

  • Chrome 8+ ( WebKitBlobBuilder ), 20+ ( Blob constructeur)
  • Firefox 6+ ( MozBlobBuilder ), 13+ ( Blob constructeur)
  • Safari 6+ ( Blob constructeur)

IE10 prend en charge MSBlobBuilder y URL.createObjectURL . Cependant, en essayant de créer un Web Worker à partir d'une blob: -L'URL génère une SecurityError.

Opera 12 ne prend pas en charge URL API. Certains utilisateurs peuvent avoir une fausse version de l URL grâce à ce piratage dans browser.js .

Fallback 1 : data-URI

Opera prend en charge les URL de données en tant qu'argument de la fonction Worker constructeur. Note : N'oubliez pas de échapper aux caractères spéciaux (Par exemple # y % ).

// response as defined in the first example
var worker = new Worker('data:application/javascript,' +
                        encodeURIComponent(response) );
// ... Test as defined in the first example

Démonstration : http://jsfiddle.net/uqcFM/37/

Solution de repli 2 : Eval

eval peut être utilisé comme solution de rechange pour Safari (<6) et IE 10.

// Worker-helper.js
self.onmessage = function(e) {
    self.onmessage = null; // Clean-up
    eval(e.data);
};
// Usage:
var worker = new Worker('Worker-helper.js');
// `response` as defined in the first example
worker.postMessage(response);
// .. Test as defined in the first example

3 votes

@BrianFreid Merci pour votre modification, mais ce n'est pas nécessaire. Si vous regardez quelques lignes plus loin, vous verrez "IE10 supports MSBlobBuilder y URL.createObjectURL . Cependant, en essayant de créer un Web Worker à partir d'une blob: -URL throws a SecurityError". Ainsi, en ajoutant MSBlobBuilder n'aura aucun effet, la seule option est la solution de repli n°2.

0 votes

Opera 12 ne définit plus URL (et donc ni l'un ni l'autre ne définit de propriétés sur lui), et le constructeur de Blob est aujourd'hui suffisamment bien supporté.

2 votes

J'ai vérifié que cela se produit toujours dans IE11, du moins dans l'aperçu.

16voto

chanu sukarno Points 723

Je suis d'accord avec la réponse acceptée actuellement, mais souvent, l'édition et la gestion du code du travailleur seront compliquées car il s'agit d'une chaîne de caractères.

Nous pouvons donc utiliser l'approche ci-dessous, qui consiste à conserver le travailleur comme une fonction, puis à le convertir en string->blob :

// function to be your worker
function workerFunction() {
    var self = this;
    self.onmessage = function(e) {
        console.log('Received input: ', e.data); // message received from main thread
        self.postMessage("Response back to main thread");
    }
}

///////////////////////////////

var dataObj = '(' + workerFunction + ')();'; // here is the trick to convert the above fucntion to string
var blob = new Blob([dataObj.replace('"use strict";', '')]); // firefox adds "use strict"; to any function which might block worker execution so knock it off

var blobURL = (window.URL ? URL : webkitURL).createObjectURL(blob, {
    type: 'application/javascript; charset=utf-8'
});

var worker = new Worker(blobURL); // spawn new worker

worker.onmessage = function(e) {
    console.log('Worker said: ', e.data); // message received from worker
};
worker.postMessage("some input to worker"); // Send data to our worker.

Ceci a été testé dans IE11+, FF et Chrome.

10voto

trusktr Points 4518

La réponse acceptée est un peu complexe, en raison de la prise en charge de la rétrocompatibilité, et je voulais donc poster la même chose, mais simplifiée. Essayez ceci dans la console de votre navigateur (moderne) :

const code = "console.log('Hello from web worker!')"
const blob = new Blob([code], {type: 'application/javascript'})
const worker = new Worker(URL.createObjectURL(blob))
// See the output in your console.

4voto

lukelalo Points 21

J'ai fait une approche en reprenant la plupart de vos idées et en ajoutant certaines des miennes. La seule chose dont mon code a besoin pour worker est d'utiliser 'this' pour faire référence à la portée de 'self'. Je suis presque sûr que cela peut être amélioré :

// Sample code
var code = function() {
    this.onmessage = function(e) {
        this.postMessage('Worker: '+e.data);
        this.postMessage('Worker2: '+e.data);
    };
};

// New thread worker code
FakeWorkerCode = function(code, worker) {
    code.call(this);
    this.worker = worker;
}
FakeWorkerCode.prototype.postMessage = function(e) {
    this.worker.onmessage({data: e});
}
// Main thread worker side
FakeWorker = function(code) {
    this.code = new FakeWorkerCode(code, this);
}
FakeWorker.prototype.postMessage = function(e) {
    this.code.onmessage({data: e});
}

// Utilities for generating workers
Utils = {
    stringifyFunction: function(func) {
        // Stringify the code
        return '(' + func + ').call(self);';
    },
    generateWorker: function(code) {
        // URL.createObjectURL
        windowURL = window.URL || window.webkitURL;   
        var blob, worker;
        var stringified = Utils.stringifyFunction(code);
        try {
            blob = new Blob([stringified], {type: 'application/javascript'});
        } catch (e) { // Backwards-compatibility
            window.BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder;
            blob = new BlobBuilder();
            blob.append(stringified);
            blob = blob.getBlob();
        }

        if ("Worker" in window) {
            worker = new Worker(windowURL.createObjectURL(blob));
        } else {
            worker = new FakeWorker(code);
        }
        return worker;
    }
};

// Generate worker
var worker = Utils.generateWorker(code);
// Test, used in all examples:
worker.onmessage = function(e) {
    alert('Response: ' + e.data);
};
function runWorker() {
    worker.postMessage('working fine');
}

Démonstration : http://jsfiddle.net/8N6aR/

2voto

Chris GW Green Points 266

Belle réponse - J'ai travaillé sur un problème similaire aujourd'hui en essayant de créer des Web Workers avec des capacités de repli lorsqu'ils ne sont pas disponibles (c'est-à-dire exécuter le worker script dans le thread principal). Comme ce fil de discussion est en rapport avec le sujet, j'ai pensé que je pourrais fournir ma solution ici :

    <script type="javascript/worker">
        //WORKER FUNCTIONS
        self.onmessage = function(event) {
            postMessage('Hello, ' + event.data.name + '!');
        }
    </script>

    <script type="text/javascript">

        function inlineWorker(parts, params, callback) {

            var URL = (window.URL || window.webkitURL);

            if (!URL && window.Worker) {

                var worker = new window.Worker(URL.createObjectURL(new Blob([parts], { "type" : "text/javascript" })));

                worker.onmessage = function(event) {
                  callback(event.data);
                };

                worker.postMessage(params);

            } else {

                var postMessage = function(result) {
                  callback(result);
                };

                var self = {}; //'self' in scope of inlineWorker. 
                eval(parts); //Converts self.onmessage function string to function on self via nearest scope (previous line) - please email chrisgwgreen.site@gmail.com if this could be tidier.
                self.onmessage({ 
                    data: params 
                });
            }
        }

        inlineWorker(
            document.querySelector('[type="javascript/worker"]').textContent, 
            {
                name: 'Chaps!!'
            },
            function(result) {
                document.body.innerHTML = result;
            }
        );

    </script>
</body>

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X