防止DOM渲染冲突的问题;
Html5中的Web Worker能够实现多线程
2.什么是任务队列?#
任务队列"是一个先进先出的数据结构,排在前面的事件,优先被主线程读取。主线程的读取过程基本上是自动的,只要执行栈一清空,"任务队列"上第一位的事件就自动进入主线程。node
2.1 同步和异步任务#
同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;
异步任务指的是,不进入主线程、而进入"任务队列"(task queue)的任务,只有"任务队列"通知主线程,某个异步任务能够执行了,该任务才会进入主线程执行。
2.2 执行流程#
全部同步任务都在主线程上执行,造成一个执行栈(execution context stack)。
主线程以外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。
一旦"执行栈"中的全部同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,因而结束等待状态,进入执行栈,开始执行。
主线程不断重复上面的第三步。express
主线程从"任务队列"中读取事件,这个过程是循环不断的,因此整个的这种运行机制又称为Event Loop(事件循环)。json
3.1 定时器函数的基本使用方法对比?#
setTimeout : 只是将事件插入了"任务队列",必须等到当前代码(执行栈)执行完,主线程才会去执行它指定的回调函数
process.nextTick : 在当前"执行栈"的尾部----下一次Event Loop(主线程读取"任务队列")以前----触发回调函数。(全部的异步任务被触发以前执行)
setImmediate:在当前"任务队列"的尾部添加事件,也就是说,它指定的任务老是在下一次Event Loop时执行,这与setTimeout(fn, 0)很像。
3.2 setImmediate和setTimeout 哪一个回调函数先执行呢?#
Copy
setImmediate(function (){
setImmediate(function A() {
console.log(1);
setImmediate(function B(){console.log(2);});
});c#
setTimeout(function timeout() {
console.log('TIMEOUT FIRED');
}, 0);
});
// 1
// TIMEOUT FIRED
// 2
[!NOTE]
上面代码中,setImmediate和setTimeout被封装在一个setImmediate里面,它的运行结果老是1--TIMEOUT FIRED--2,这时函数A必定在timeout前面触发。至于2排在TIMEOUT FIRED的后面(即函数B在timeout后面触发),是由于setImmediate老是将事件注册到下一轮Event Loop,因此函数A和timeout是在同一轮Loop执行,而函数B在下一轮Loop执行。数组
3.3 process.nextTick和setImmediate的区别?#
多个process.nextTick语句老是在当前"执行栈"一次执行完,多个setImmediate可能则须要屡次loop才能执行完。缓存
V8引擎解析JavaScript脚本。
解析后的代码,调用Node API。
libuv库负责Node API的执行。它将不一样的任务分配给不一样的线程,造成一个Event Loop(事件循环),以异步的方式将任务的执行结果返回给V8引擎。
V8引擎再将结果返回给用户。数据结构
5.1 Node的单线程#
Node.js 是以单线程的模式运行的,但它使用的是事件驱动来处理并发,这样有助于咱们在多核 cpu 的系统上建立多个子进程,从而提升性能。多线程
每一个子进程老是带有三个流对象:child.stdin, child.stdout 和child.stderr。他们可能会共享父进程的 stdio 流,或者也能够是独立的被导流的流对象。架构
Node 提供了 child_process 模块来建立子进程并发
5.2 建立进程的方法#
exec - child_process.exec 使用子进程执行命令,缓存子进程的输出,并将子进程的输出以回调函数参数的形式一次性返回。exec方法会从子进程中返回一个完整的buffer。默认状况下,这个buffer的大小应该是200k。若是子进程返回的数据大小超过了200k,程序将会崩溃,同时显示错误信息“Error:maxBuffer exceeded”。你能够经过在exec的可选项中设置一个更大的buffer体积来解决这个问题,可是你不该该这样作,由于exec原本就不是用来返回不少数据的方法。
spawn - child_process.spawn 使用指定的命令行参数建立新进程。spawn 会返回一个带有stdout和stderr流的对象。你能够经过stdout流来读取子进程返回给Node.js的数据。stdout拥有’data’,’end’以及通常流所具备的事件。当你想要子进程返回大量数据给Node时,好比说图像处理,读取二进制数据等等,你最好使用spawn方法。
fork - child_process.fork 是 spawn()的特殊形式,用于在子进程中运行的模块,如 fork(‘./son.js’) 至关于 spawn(‘node’, [‘./son.js’]) 。与spawn方法不一样的是,fork会在父进程与子进程之间,创建一个通讯管道,用于进程之间的通讯。
5.3 实例分析#
5.3.1 exec#
Copy
require('child_process').exec('dir', {encoding: ‘utf-8’}, function(err, stdout, stderr) {
if (err) { console.log(error.stack); console.log('Error code: ' + error.code); console.log('Signal received: ' + error.signal); } //console.log(err, stdout, stderr); console.log('data : ' + stdout);
}).on('exit', function (code) {
console.log('子进程已退出, 退出码 ' + code);
});
5.3.2 spawn#
Copy
var child_process = require('child_process');
var spawnObj = child_process.spawn('ping', ['127.0.0.1'], {encoding: 'utf-8'});
spawnObj.stdout.on('data', function(chunk) {
console.log(chunk.toString());
});
spawnObj.stderr.on('data', (data) => {
console.log(data);
});
spawnObj.on('close', function(code) {
console.log('close code : ' + code);
}
spawnObj.on('exit', (code) => {
console.log('exit code : ' + code); fs.close(fd, function(err) { if(err) { console.error(err); } });
});
5.3.3 fork#
分为 “父进程”(parent.js) 和”子进程”(child.js)。在命令行执行的时候要切换到上述文件的目录中,不然会找不到子进程。
parent.js
Copy
console.log('parent pid: ' + process.pid);
var fork = require('child_process').fork;
//fork方法返回的是子进程
var child = fork('./child.js');
console.log('fork return pid: ' + child.pid);
child.on('message', function(msg){
console.log('parent get message: ' + JSON.stringify(msg));
});
child.send({key: 'parent value'});
child.js
Copy
console.log('child pid: ' + process.pid);
process.on('message', function(msg){
console.log('child get message: ' + JSON.stringify(msg));
});
process.send({key: 'child value'});
Express 是一个Node.js的基础框架,主要基于 Connect 中间件,而且自身封装了路由(须要配合bodyParser)、视图处理等功能,使用人数众多,弊端是callback回调方式。
Koa 是一个比Express更精简,使用node新特性的中间件框架。其提供的是一个架子,而几乎全部的功能都须要由第三方中间件完成,好比koa-router, koa-view等。
[!NOTE]
Koa 利用 co 做为底层运行框架,利用 Generator 的特性,实现“无回调”的异步处理
6.1 处理路由#
6.1.1 Express#
[!NOTE]
使用 express.Router 类来建立可安装的模块化路由处理程序。Router 实例是完整的中间件和路由系统,如下示例将路由器建立为模块,在其中装入中间件,定义一些路由,而后安装在主应用程序的路径中。
Copy
var express = require('express');
var router = express.Router();
router.use(function timeLog(req, res, next) {
console.log('Time: ', Date.now()); next();
});
// define the home page route
router.get('/', function(req, res) {
res.send('Birds home page');
});
// define the about route
router.get('/about', function(req, res) {
res.send('About birds');
});
module.exports = router;
接着,在应用程序中装入路由器模块:
Copy
var routes = require('./route');
...
app.use('/route', routes);
6.1.2 Koa#
路由处理 Express 是自身集成的,而 Koa 须要引入中间件
Copy
var koa = require('koa')
var route = require('koa-route') //中间件
var app = koa()
app.use(route.get('/', function *(){
this.body = 'Hello World'
}))
6.2 HTTP Request#
[!NOTE]
两个框架都封装了HTTP Request对象,有一点不一样是 Koa v1 使用 this 取代 Express 的 req、res。
6.2.1 Express#
Copy
var app = require('express')()
app.get('/room/:id', function (req, res) {
console.log(req.params)
})
// 获取POST数据须要 body-parser 中间件
var bodyParser = require('body-parser')
app.use(bodyParser.json())
app.post('/sendgift', function (req, res) {
console.log(req.body)
})
6.2.2 Koa#
Copy
var app = require('koa')()
var route = require('koa-route')
app.use(route.get('/room/:id', function *() {
console.log(this.req.query)
}))
// 获取POST数据须要 co-body 中间件
var parse = require('co-body')
app.use(route.post('/sendgift', function *() {
var post = yield parse(this.request) console.log(post)
}))
6.3 区别#
6.3.1 异步流程控制#
Express 采用 callback 来处理异步,Koa v1 采用 generator,Koa v2 采用 async/await。
6.3.2 错误处理#
Express 使用 callback 捕获异常,对于深层次的异常捕获不了,
Koa 使用 try catch,能更好地解决异常捕获。
Copy
// Express callback
app.use(function (err, req, res, next) {
console.error(err.stack) res.status(500).send('Something broke!')
})
// Koa generator
app.use(function *(next) {
try { yield next } catch (err) { this.status = err.status || 500 this.body = { message: err.message } this.app.emit('error', err, this) }
})
// Koa async/await
app.use(async (ctx, next) => {
try { await next() } catch (err) { ctx.status = err.status || 500 ctx.body = { message: err.message } ctx.app.emit('error', err, this) }
})
6.3.3 中间件处理#
Express中app.use就是往中间件数组中塞入新的中间件,中间件处理方式是线性的,next事后继续寻找下一个中间件。 一个请求进来通过一系列中间件处理后再响应给用户,清晰明了。
缺点:基于 callback 组合业务逻辑,业务逻辑复杂时嵌套过多,异常捕获困难。
Koa的中间件处理方式是一个洋葱模型,koa处理完中间件后还会回来走一趟,这就给了咱们更加大的操做空间。
Copy
const Koa = require('koa');
const app = new Koa();
// x-response-time
app.use(async (ctx, next) => {
const start = Date.now(); await next(); const ms = Date.now() - start; ctx.set('X-Response-Time', `${ms}ms`);
});
// logger
app.use(async (ctx, next) => {
const start = Date.now(); await next(); const ms = Date.now() - start; console.log(`${ctx.method} ${ctx.url} - ${ms}`);
});
// response
app.use(async ctx => {
ctx.body = 'Hello World';
});[!NOTE]当koa处理中间件遇到await next()的时候会暂停当前中间件进而处理下一个中间件,最后再回过头来继续处理剩下的任务