Worker多线程具体使用示例

<!doctype html>
<html lang="zh">
<head>
    <meta charset="utf-8">
    <title>Worker多线程具体使用示例</title>
</head>
<body>
<script type="text/javascript">
    let worker = new Worker('worker.js');

    worker.onerror = function (event) {
        console.log('子线程[worker.js]出错辣~~~', event);
    };

    // 接收worker子线程消息
    worker.onmessage = function (event) {
        let data = event.data; // 获取消息内容

        //========== 处理子线程消息:哈希运算 ==========//
        if (data.action === 'hash') {
            console.log('md5(\'%s\')    ===> ', data.str, data.md5);
            console.log('sha512(\'%s\') ===> ', data.str, data.sha512);
            // md5('Worker')    ===>  62efb9ec331e364b96efe68c8b03ca20
            // sha512('Worker') ===>  772053c8c3ea79e68515dab11bf9950cfdbe163ccc5bc08acafd2b91eef04bccffcfbac7abd04ea480d218……

            return;
        }

        //========== 处理子线程消息:AJAX请求 ==========//
        if (data.action === 'ajax') {
            let response = data.response;

            if (response.errcode === 0) {
                let name = response.name;
                let gender = response.gender;
                let age = response.age;
                console.log('俺叫%s(%s),今年%d岁。', name, gender, age); // 俺叫张三(男),今年18岁。
            } else {
                alert(response.errmsg);
            }

            return;
        }

        //========== 处理子线程消息:其它 ==========//
        // TODO...
    };

    // 向worker子线程发送消息(哈希运算)
    worker.postMessage({action: 'hash', str: 'Worker'}); // 可直接将对象作为参数,不需要转为字符串(下同)

    // 向worker子线程发送消息(AJAX请求)
    worker.postMessage({
        action: 'ajax',

        // 设置AJAX请求的option,这里仿照jQuery的$.ajax({...})写法
        options: {
            url: './ajax.php',
            method: 'POST',
            responseType: 'json',
            data: {uid: 9527},
            async: true,
            timeout: 1000 * 30,
            // beforeSend: function (xhr) {   // 重要提醒:消息不能传递function类型,否则会报“could not be cloned”错误,也就是
            //     console.log(xhr);                       说想在子线程里执行beforeSend、success等回调操作是行不通的,只能老老实
            // },                                          实让子线程把响应结果发回给主线程,由主线程来处理响应结果。
            // success: function (response) {
            //     console.log(response);
            // },
        }
    });

    // 销毁worker子线程
    // worker.terminate();
</script>
</body>
</html>

//========== worker.js ==========//

// 在子线程里也可以加载其它JavaScript文件,且可以一次加载多个文件
importScripts('function.lib.js', 'util.js');

// 接收主线程消息
self.onmessage = function (event) {
    let data = event.data; // 获取消息内容

    // 向主线程发送消息(延迟执行模拟耗时操作)
    setTimeout(function () {
        //========== 处理主线程消息:哈希运算 ==========//
        if (data.action === 'hash') {
            let message = {};
            message.action = data.action;
            message.str = data.str;
            message.md5 = md5(data.str);
            message.sha512 = sha512(data.str);
            self.postMessage(message);

            return;
        }

        //========== 处理主线程消息:AJAX请求 ==========//
        if (data.action === 'ajax') {
            let xhr = new XMLHttpRequest(); // 创建XMLHttpRequest对象
            let options = data.options;     // AJAX请求的option

            // 服务器响应内容类型,可选值:'' | 'arraybuffer' | 'blob' | 'document' | 'json' | 'text'
            xhr.responseType = options.responseType;

            // 请求超时时间,单位:毫秒
            xhr.timeout = options.timeout;

            // 请求完成后的回调函数(注意:请求完成不是请求成功,也可能是请求失败或请求超时)
            xhr.onreadystatechange = function () {
                let message = {};
                message.action = data.action;

                // 设置通用错误响应,后续会根据状态码进一步判断是什么错误
                message.response = {errcode: 1, errmsg: '请求失败'};

                if (xhr.readyState === XMLHttpRequest.DONE) {
                    // 根据状态码判断是什么错误
                    switch (xhr.status) {

                        case 0: {
                            message.response.errmsg = '请求超时';
                            break;
                        }

                        case 200: {
                            message.response = xhr.response; // 请求成功,将响应结果原样附加进消息然后发回给主线程即可
                            break;
                        }

                        case 404: {
                            message.response.errmsg = '404 Not Found';
                            break;
                        }

                        case 500: {
                            message.response.errmsg = '500 Internal Server Error';
                            break;
                        }

                        default: {
                            message.response.errmsg = '请求失败(' + xhr.status + ')';
                        }

                    }

                    self.postMessage(message);
                }
            };

            // 创建请求(注意:此时还未发起请求)
            xhr.open(
                options.method, // 请求方法,可选值:GET|POST|PUT|DELETE
                options.url,    // 请求URL
                options.async   // 是否异步
            );

            // 设置POST参数
            let formData = new FormData();
            Object.entries(options.data).forEach(function ([key, value]) {
                formData.append(key, value);
            });

            // 带上POST参数发起请求
            xhr.send(formData);

            return;
        }

        //========== 处理主线程消息:其它 ==========//
        // TODO...
    }, 3000);
};

//========== function.lib.js ==========//

/**
 * 计算字符串的md5散列值
 *
 * @param str string 字符串
 * @return string 字符串的md5散列值
 */
function md5(str) {
    // 由于本文核心是演示如何使用Worker多线程,这里就不具体实现md5逻辑,直接返回一串字符串
    return '62efb9ec331e364b96efe68c8b03ca20';
}

/**
 * 计算字符串的sha512散列值
 *
 * @param str string 字符串
 * @return string 字符串的sha512散列值
 */
function sha512(str) {
    // 由于本文核心是演示如何使用Worker多线程,这里就不具体实现sha512逻辑,直接返回一串字符串
    let hash = '';
    hash += '772053c8c3ea79e68515dab11bf9950cfdbe163ccc5bc08acafd2b91eef04bcc';
    hash += 'ffcfbac7abd04ea480d218382ee404b30380a3871d1004c7bc2fbb067a1efb5a';
    return hash;
}

//========== util.js ==========//

// TODO...

<?php
//========== ajax.php ==========//

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    // 这里模拟根据UID获取用户信息
    $return = ['errcode' => 1, 'errmsg' => '用户不存在'];
    $uid = isset($_POST['uid']) ? (int)$_POST['uid'] : 0;
    if ($uid === 9527) {
        $return['errcode'] = 0;
        $return['errmsg'] = '';
        $return['name'] = '张三';
        $return['gender'] = '男';
        $return['age'] = 18;
    }

    try {
        header('Content-Type:application/json; charset=utf-8');
        echo json_encode($return, JSON_THROW_ON_ERROR | 320);
    } catch (JsonException $e) {
        header('Content-Type:text/html; charset=utf-8');
        echo 'JsonException: ' . $e->getMessage();
    }
}



  根据子线程不存在window对象的特点,可以把主线程代码和子线程代码写在同一个JavaScript文件里,如下面的代码:

//========== 由主线程执行的代码 ==========//
if (typeof window !== 'undefined') {
    // 获取当前JavaScript脚本URL,并创建子线程执行当前JavaScript脚本里的子线程代码
    let scriptURL = document.scripts[document.scripts.length - 1].src;
    let worker = new Worker(scriptURL);

    worker.onerror = function (event) {
        console.log('子线程出错辣~~~', event);
    };

    // 接收子线程消息
    worker.onmessage = function (event) {
        console.log('主线程收到子线程消息:' + event.data);
    };

    // 向子线程发送消息
    worker.postMessage('PHP');

    // 销毁子线程
    // worker.terminate();
}


//========== 由子线程执行的代码 ==========//
if (typeof window === 'undefined') {
    // 接收主线程消息
    self.onmessage = function (event) {
        console.log('子线程收到主线程消息:' + event.data);

        // 向主线程发送消息(延迟执行模拟耗时操作)
        setTimeout(function () {
            self.postMessage(event.data + '·从入门到放弃'); // 向主线程发送消息
            self.close(); // 关闭子线程
        }, 2500);
    };
}


//========== 控制台输出结果·开始 ==========//
// 子线程收到主线程消息:PHP
// 主线程收到子线程消息:PHP·从入门到放弃
//========== 控制台输出结果·结束 ==========//

Copyright © 2024 码农人生. All Rights Reserved