JavaScript异步编程:设计快速响应的网络应用

js中对事件的处理是单线程的。

        var start = new Date;
        setTimeout(function () {
            var end = new Date;
            console.log(end- start);//这个数值大于1000,因为js是单线程的。否则500ms后将会在第二个线程中运行到此处,导致此处为500+
        },500);
        while(new Date - start < 1000){}
调用setTimeout的时候,有一个延时事件排入队列。然后setTimeout之后的代码执行,直到没有更多代码时,才会开始执行队列中符合条件的事件。

setTimeout和setInterval的缺点是有最短间隔时间至少4ms。


异步函数

回调

非阻塞


PubSub发布订阅设计模式

PubSub = {handlers: {}}
PubSub.on = function (eventType, handler) {
    if (!(eventType in this.handlers)) {
        this.handlers[eventType] = [];
    }
    this.handlers[eventType].push(handler);
    return this;
}

PubSub.emit = function (eventType) {
    var handlerArgs = Array.prototype.slice.call(arguments, 1);//获取触发事件时,传入的除第一个参数外的参数
    //循环执行对应的事件处理函数
    for (var i = 0; i < this.handlers[eventType].length; i++) {
        this.handlers[eventType][i].apply(this, handlerArgs);
    }
    return this;
}
Node的EventEmitter对象的部分核心部分。

说到工具栏的搞法

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="jquery-3.1.1.js"></script>
    <script>
        $(function () {
            var createMenu = function () {
                var jEvent = arguments[0],
                    x = jEvent.offsetX,
                    y = jEvent.offsetY,
                    container = $('.container'),
                    menu = null,
                    timeStr = new Date().toString();
                container.one('clearMenu', function () {
                    $('.menu').remove();
                });
                container.trigger('clearMenu');
                menu = $('<div class="menu"><ul><li>菜单</li><li>' + timeStr + '</li></ul></div>');
                menu.css({
                    top: y, left: x
                });
                container.append(menu);
            };
            $('.container').on('click', createMenu);
        });
    </script>
    <style>
        .container {
            border: 1px solid #f00;
            width: 700px;
            height: 700px;
        }

        .menu {
            position: absolute;
            top: 0;
        }
    </style>
</head>
<body>
<div class="container"></div>
</body>
</html>

解决一次性事件的工具Promise

jQuery中的Deferred对象,是Promise的超集。

/**
             * 先完成动画,然后依次执行promise的done
             */
            function testAniamte() {
                var $p = $('p');
                var p1 = $p.hide('slow').promise();
                var p2 = $p.show('slow').promise();
                var p3 = $p.hide('slow').promise();
                var p4 = $p.show('slow').promise();
                p1.done(function () {alert(1);});
                p2.done(function () {alert(2);});
                p3.done(function () {alert(3);});
                p4.done(function () {alert(4);});
            }

            /**
             * 将Deferred对象的resolve方法作为动画的回掉,可以生成行为完全相同的动画版Promise对象
             */
            function testAniamte2() {
                var def1 = $.Deferred(),def2 = $.Deferred(),def3 = $.Deferred(),def4 = $.Deferred(), $p = $('p');
                $p.hide('slow',def1.resolve);
                def1.done(function () {alert(1);});
                $p.show('slow',def2.resolve);
                def2.done(function () {alert(2);});
                $p.hide('slow',def3.resolve);
                def3.done(function () {alert(3);});
                $p.show('slow',def4.resolve);
                def4.done(function () {alert(4);});
            }

var def = $.Deferred();//获取一个Deferred对象

然后可以在这个对象上面搞事。

def.done(callback1).fail(callback2).always(callbacks);

在调用def的函数中使用def.resolve()来回调done(),使用reject()来回调fail().使用def.promise()来获取一个无法改变状态,只能绑定回调的Promise对象

var wait = function () {
                    var dtd = $.Deferred(); // 新建一个Deferred对象
                    var tasks = function () {
                        dtd.notify(new Date().toString());
                        alert("执行完毕!");
                        dtd.resolve("mrz"); // 改变Deferred对象的执行状态
                        dtd.done(function () {
                            alert("马上执行");
                        });
                    };
                    dtd.notify(new Date().toString());
                    setTimeout(tasks, 1000);
                    return dtd.promise();
                };
                var promise = wait();
                promise.done(function (msg) {
                        alert("哈哈,成功了!"+msg);
                    })
                    .fail(function () {
                        alert("出错啦!");
                    }).progress(function (dateStr) {
                    console.log(dateStr);
                })
                //dtd.resolve();

上述代码偷自http://www.ruanyifeng.com/blog/2011/08/a_detailed_explanation_of_jquery_deferred_object.html

$.when()需要给的Promise都已执行才会执行自己的Promise。。。

pipe()


Async.js异步处理的一个库(文档

从同步读取文件讲起,说到异步读取文件更快。

用到了async.filter来过滤目录,获取到文件后使用async.forEachSeries来获取所有文件内容。

/***
 * Excerpted from "Async JavaScript",
 * published by The Pragmatic Bookshelf.
 * Copyrights apply to this code. It may not be used to create training material, 
 * courses, books, articles, and the like. Contact us if you are in doubt.
 * We make no guarantees that this code is fit for any purpose. 
 * Visit http://www.pragmaticprogrammer.com/titles/tbajs for more book information.
***/
var async = require('async');
var fs = require('fs');
process.chdir('recipes');  // change the working directory

var concatenation = '';

var dirContents = fs.readdirSync('.');

async.filter(dirContents, isFilename, function(filenames) {
  async.forEachSeries(filenames, readAndConcat, onComplete);
});

function isFilename(filename, callback) {
  fs.stat(filename, function(err, stats) {
    if (err) throw err;
    callback(stats.isFile());
  });
}

function readAndConcat(filename, callback) {
  fs.readFile(filename, 'utf8', function(err, fileContents) {
    if (err) return callback(err);
    concatenation += fileContents;
    callback();
  });
}

function onComplete(err) {
  if (err) throw err;
  console.log(concatenation);
}

但是在当前版本的async(3.10.10)下代码无输出,查看文档发现需要添加一个参数err两处改为callback(err,stats.isFile());和function(err,filenames)

说到了NodeJs的错误处理技术,没学过所以现在还没接触所以有点迷糊,回头来看这个。

.Async的任务组织技术

一组需要按顺序执行的异步函数时,使用async.series和async.waterfall

var async = require('async')
var start = new Date();
async.waterfall([
    function (callback) {
        setTimeout(callback, 100);
    },
    function (callback) {
        setTimeout(callback, 200);
    },
    function (callback) {
        setTimeout(callback, 300);
    }
], function (err, results) {
    console.log('async.waterfall time:'+(new Date() - start));
});

start = new Date();
async.waterfall([
    function (callback) {
        setTimeout(function () {
            return callback(null, 1, 2);
        }, 100);
    },
    function (arg1, arg2, callback) {
        console.log('async.waterfall2:'+(arg1 + arg2));
        setTimeout(function () {
            return callback(null, "qqf");
        }, 200);
    },
    function (arg1, callback) {
        console.log('async.waterfall2:'+arg1);
        setTimeout(function () {
            return callback(null, "qo");
        }, 300);
    }
], function (err, results) {
    console.log('async.waterfall2 time :'+(new Date() - start));
    console.log('async.waterfall2 result:'+results);//只有最后一个函数传递的值
});


start = new Date();
async.series([
    function (callback) {
        setTimeout(function () {
            return callback(null, 1, 2);
        }, 100);
    },
    function (callback) {
        setTimeout(function () {
            return callback(null, "qqf");
        }, 200);
    },
    function (callback) {
        setTimeout(function () {
            return callback(null, "qo");
        }, 300);
    }
], function (err, results) {
    console.log('async.series result:'+(new Date() - start));
    console.log('async.series result:'+results);//串起来所有传递的参数
});

async.series([
    function (callback) {
        return callback(null, 1, 2);
    },
    function (callback) {
        return callback(null, "qqf");
    },
    function (callback) {
        return callback(null, "qo");
    }
], function (err, results) {
    console.log('async.series2 result:')
    console.log(results);//串起来所有传递的参数
});

结果如下:

async.series2 result:
[ [ 1, 2 ], 'qqf', 'qo' ]
async.waterfall2:3
async.waterfall2:qqf
async.waterfall time:625
async.waterfall2 time :625
async.waterfall2 result:qo
async.series result:625
async.series result:1,2,qqf,qo

4.3.1异步函数的并行运行

将上面代码中的series改为parallel即可,多个任务将同时开始运行,并将所有传递的给回调的参数,按照传递事件的顺序串起来(并不按照执行顺序)

4.4异步工作流的动态排队技术      async.queue

var async = require('async');

function worker(task,callback) {
    console.log('获取到的数据是:'+task.data+',然后可以做一些事情');
    callback();
}
var concurrency = 2;//可以同时执行的任务数,并发数
var queue = async.queue(worker,concurrency);
queue.push({data:1});
queue.push({data:2});
queue.push({data:3});
queue.push({data:4});

输出

获取到的数据是:1,然后可以做一些事情
获取到的数据是:2,然后可以做一些事情
获取到的数据是:3,然后可以做一些事情
获取到的数据是:4,然后可以做一些事情

queue的push方法,参数如果是数组,也是一个个传递的,可以传递函数和其他东西。

如果需要传递函数,是的如同series和,parallel一样则

function worker(task,callback){
    task(callback);
}

push()的第二个参数是回调函数,当当前的任务执行完毕时触发

当队列完成时的回调函数 为queue.drain属性,触发此回调之后,再次push,完成后还会触发此回调

var start = new Date ;
function functionWorker(data, callback) {
    setTimeout(callback,data);
}
var functionQueue = async.queue(functionWorker,2);
functionQueue.drain = function(){
    console.log("全部完成~~~");
    console.log(new Date - start);
};
functionQueue.push(100);
functionQueue.push(200);
functionQueue.push(300);


当并发数分别为3,2,1时,大概运行时间为 328,453,636



4.5Step工作流控制

。。。


Worker对象的多线程技术

cluster

var cluster = require('cluster');
if (cluster.isMaster) {
    var coreCount = require('os').cpus().length;
    for (var i = 0; i < coreCount; i++) {
        cluster.fork();
    }
    cluster.on('exit', function (worker) {
        console.log(`worker ${worker.process.pid} 死了`);
    });
}else{
    process.exit();
}
API中两处改变
worker对象与主进程通信

var cluster = require('cluster');
if (cluster.isMaster) {
    var coreCount = require('os').cpus().length;
    for (var i = 0; i < coreCount; i++) {
        var worker = cluster.fork();
        worker.send('你是我儿子哟');
        worker.on('message',function (message) {
            if(message._queryId) return;//忽略内部消息
            console.log(message);
        });
    }
    cluster.on('exit', function (worker) {
        console.log(`worker ${worker.process.pid} 死了`);
    });
}else{
    process.send('papa~');
    process.on('message',function (message) {
        console.log(message);
    })
}

papa~
你是我儿子哟
papa~
你是我儿子哟

或者其他顺序都是正常的,因为这个线程的抢占是随机的。


浏览器端的脚本异步加载

<script>标签的defer/async  都不会暂停页面的加载和dom的解析

defer  在后台加载脚本,直到页面加载完毕之后才执行

async  不管位置顺序,只要加载好了就开始执行。一般在第三方的插件来使用,因为有他没他不是很重要。

参考defer、async属性以及JS异步加载并执行解决方案  【http://blog.csdn.net/q121516340/article/details/51436314】

https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/script


yepnope库 简单小巧的库

Require.js 用过。虽然当时不明白这是个啥。囧






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值