Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

【2016-04-10】尝试用webworker优化体验 #29

Open
toxic-johann opened this issue Aug 3, 2017 · 0 comments
Open

【2016-04-10】尝试用webworker优化体验 #29

toxic-johann opened this issue Aug 3, 2017 · 0 comments

Comments

@toxic-johann
Copy link
Owner

之前编写的ascii化网站会存着太大运算量导致进程卡死页面假死的问题。其实这个问题在业界已经有很好的解决方法。调用webworker分派到其他进程进行运算即可。于是我就在这个项目上使用了一下web worker。

  • worker-loader

因为web worker的脚本是在页面生成后再进行拉取的。而我在页面中使用的是webpack页面打包机制。两者给我的感觉理念使用相悖的。

其实如果没什么特别需求,可以直接在页面上使用正常的webworker方式进行处理。

var myWorker = new Worker("my_task.js");

myWorker.onmessage = function (oEvent) {
  console.log("Called back by the worker!\n");
};

个人认为也是没什么问题。不过我比较懒,想在webpack打包的时候把所有问题解决掉。

而且我并不想破坏我的一些插件的内部结构。所以我希望将插件打包到我的worker中,然后再进行处理。

于是我尝试了使用worker-loader插件。

配置的方法不算难。

module.exports = {
    entry: {
        "/page/home/index/ascii": dir + '/page/home/index/ascii.js',
        "/page/home/index/photoshop": dir + '/page/home/index/photoshop.js',
        
    },
    output: {
        path: dir + "/../www/static/js/",
        filename: "[name].js",
        publicPath:"/static/js/"
    },
    module: {
        loaders: [
            {
              test: /\.js$/,
              exclude: /(node_modules|bower_components)/,
              loader: 'babel',
              query: {
                    presets: ['es2015']
                }
            },
            //在此处加上worker-loader即可
            { 
                test: /\.worker$/,
                loader: "worker-loader",
            },
        ]
    },
    externals: {
        // require("jquery") 是引用自外部模块的
        // 对应全局变量 jQuery
        "jquery": "jQuery",
        "wx":"wx",
    }
}

使用的时候,加上一个前缀。

let MyWorker = require("worker!./ascii-worker.js");

let myWorker = new MyWorker();

就会进行自动编译。不过这个插件,貌似没有办法进行命名。而且会很自觉地放在公共路径下面。这样子会导致派生出很多个文件。需要进行清理。

不过这个问题不大,并不会影响什么。但是以后看看有没有办法解决掉吧。这是后话。

  • web worker文件的配置

我的做法是将webworker作为一个中转站。根据传入的事件,进行相关事件的处理,返回需要的数据。

我们先看一下我写的代码。

let Photoshop = require("../../../utils/photoshop.js");
let photoshop = new Photoshop;

let AsciiPic = require("../../../utils/asciiPic.js");
let asciiPic = new AsciiPic;

首先,在webworker引入我需要的处理用的插件。

onmessage = function (oEvent) {
    let ret = {
        behavior:"出错了~~~",
        data:null
    };
    if(oEvent.data.behavior == "sketch"){
        let pic = photoshop.sketch.apply(photoshop,oEvent.data.args);
        ret = {
            behavior:oEvent.data.behavior,
            data:[pic]
        }
    } else if (oEvent.data.behavior == "adjustContrast"){
        let pic = photoshop.adjustContrast.apply(photoshop,oEvent.data.args);
        ret = {
            behavior:oEvent.data.behavior,
            data:[pic]
        }
    } else if(oEvent.data.behavior == "asciiFromImagedata"){
        let pic = asciiPic.asciiFromImagedata.apply(asciiPic,oEvent.data.args);
        ret = {
            behavior:oEvent.data.behavior,
            data:[pic]
        }
    }
    postMessage(ret);
};

// webpack打包所需
module.exports = {};

然后就是一个事件处理。onmessage是浏览器给我们提供的一个事件机制,我们可以监听到主程序发出的指令。然后我在事件中定义一个标记,根据标记不同,进行不同的处理。

我将参数作为数据传入,然后利用apply将参数传给执行者。

最后利用postMessage返回。

postMessage也是浏览器提供的与主进程之间处理的一个方法。

就这样,我完成了一个中转站的设置。

  • 主进程设置

那么在主进程中,我们也是同样的建立一个事件机制即可。

let MyWorker = require("worker!./ascii-worker.js");
let ToxicEvents = require("../../../utils/events.js");

let myWorker = new MyWorker();
let events = new ToxicEvents();

// 设定沟通之间的事件监听
myWorker.onmessage = function (oEvent) {
    events.trigger(oEvent.data.behavior,oEvent.data.data);
};

为此,我写了一个小的事件模型进行处理。

当我收到worker的信息后,将信息作为一个事件转发。并且带上参数。

那么我在主进程中就可以进行监听和处理了。举个简单的栗子。

tips("正在素描化中")
let radius = parseInt($("#radius").val());
myWorker.postMessage({
    behavior:"sketch",
    args:[imageData,radius]
})

events.on("sketch",function(evt,data){
    imageData.data.set(data[0]);
    drawImageData(imageData);
    events.off(evt.type,evt.id);
    tips("完成",null,"hide");
})

用myworker.postMessage通知web worker进行处理。然后他处理完后会触发我的事件机制,将结果传入。然后我作出页面响应。

  • 同样一些需要注意的细节

在v8的strong mode里面arguments被禁用掉了。因此在webpack的打包中,你会发现使用arguments会拿不到结果。

这个对于我们一些参数转移并不是很友好。辣么怎么办呢。使用...args吧。而且args会是一个真正的数组。也很方便。

this.trigger = (...args)=>{
    let self = this;
    let type = args[0];
    if(!type){
        throw new Error("事件触发需申明事件类型");
    }
    if(!_events[type]){
        console.warn("该类型没有绑定事件");
        return;
    }
    _events[type].forEach(each=>{
        let tmpArgs = args;
        tmpArgs.shift();
        tmpArgs.unshift({
            type:each.type,
            id:each.id,
        })
        each.listener.apply(self,tmpArgs);
    });
}

在我的事件机制里面trigger就是用了这个方法。这样子我可以成功将用户绑定的多个参数传回去给用户。

最后由于大家在交大的节日已经很excited了。所以这次就来一个乔帮主吧。

我和华莱士当年也是谈笑风生的

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant