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 用过。虽然当时不明白这是个啥。囧