# 二.执行栈
前言
js 编译成浏览器可以运行的代码后,需要创建一个环境来运行代码,这个环境就是执行栈
在执行栈中先执行同步代码,将代码压入栈中,函数包裹的代码等执行后压入,等全部同步代码执行完成后,栈内代码从栈顶向下执行,依次出栈,执行时在执行上下文中区相关变量
# 1.同步代码
# 2.异步代码
- 回调函数
- 事件监听
- 发布订阅
- Promise/A+ 和生成器函数
- async/await
# 2.1 回调函数
所谓回调函数,就是把任务的第二段写在一个函数里面,等到重新执行这个任务的时候,就直接调用这个函数
fs.readFile("某个文件", function (err, data) {
if (err) throw err
console.log(data)
})
1
2
3
4
2
3
4
这是一个错误优先的回调函数,这也是 Node.js 本身的特点之一。
回调的问题
- 1.异步代码时
try catch
不再生效
let async = function(callback){
try{
setTimeout(function(){ callback() },1000)
}catch(e){
console.log('捕获错误',e) // Node 在处理异常有一个约定,将异常作为回调的第一个实参传回,如果为空表示没有出错
}
}
async(function(t){ console.log(t) }) // 这个回调函数被存放了起来,直到下一个事件环的时候才会取出,try 只能捕获当前循环内的异常,对 callback 异步无能为力
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
异步方法也要遵循两个原则
- 必须在异步之后调用传入的回调函数
- 如果出错了要向回调函数传入异常供调用者判断
let async = function (callback) {
try {
setTimeout(function (success) {
if (success) {
callback(null)
} else {
callback("错误")
}
}, 1000)
} catch (e) {
console.log("捕获错误", e)
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
- 2.回调地狱
let fs = require("fs")
fs.readFile("template.txt", "utf8", function (err, template) {
// 异步多级依赖的情况下嵌套非常深,代码难以阅读维护
fs.readFile("data.txt", "utf8", function (err, data) {
console.log(template + " " + data)
})
})
1
2
3
4
5
6
7
2
3
4
5
6
7
# 2.2 事件发布/订阅模型
订阅事件实现了一个事件与多个回调函数的关联
let fs = require("fs")
let EventEmitter = require("events")
let eve = new EventEmitter()
let html = {}
eve.on("ready", function (key, value) {
html[key] = value
if (Object.keys(html).length == 2) {
console.log(html)
}
})
function render() {
fs.readFile("template.txt", "utf8", function (err, template) {
eve.emit("ready", "template", template)
})
fs.readFile("data.txt", "utf8", function (err, data) {
eve.emit("ready", "data", data)
})
}
render()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 2.3 哨兵变量
let fs = require("fs")
let after = function(itmers, callback) {
let result = {}
return function(key, value) {
result[key] = value
if (Object.keys(result).length == items) {
callback(result)
}
}
}
let done = after(2,functin(result){
console.log(result)
})
function render(){
fs.readFile('template.txt','utf8',function(err,template){
done('template',template)
})
fs.readFile('data.txt','utf8',function(err,data){
done('data',data)
})
}
render()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 2.4 Promise/Deferred 模式
# 2.5 生成器 Generators/yield
- 当你在执行一个函数的时候,你可以在某个点暂停函数的执行,并且做一些其他工作,然后再返回这个函数继续执行,甚至是携带一些新的值,然后继续执行。
- 上面描述的场景正是 JavaScript 生成器函数所致力于解决的问题。当我们调用一个生成器函数的时候,它并不会立即执行,而是需要我们手动去执行迭代操作(next 方法)。也就是说,你调用生成器函数,它会返回给你一个迭代器。迭代器会遍历每个终端点。
- next 方法返回值的 value 属性,是 Generator 函数向外输出数据;next 方法还可以接受参数,这是向 Generator 函数体内输入数据。
# 6.4.1.生成器的使用
function* foo() {
var index = 0
while (index < 2) {
yield index++
}
}
var bar = foo()
console.log(bar.next())
console.log(bar.next())
console.log(bar.next())
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
# 6.4.2.Co
co
是一个为Node.js
和浏览器打造的基于生成器的流程控制工具,借助于 Promise,你可以使用更加优雅的方式编写非阻塞代码。
let fs = require("fs")
function readFile(filename) {
return new Promise(function (resolve, reject) {
fs.readFile(filename, function (err, data) {
if (err) reject(err)
else resolve(data)
})
})
}
function* read() {
let template = yield readFile("./template.txt")
let data = yield readFile("./data.txt")
return template + "+" + data
}
co(read).then(
function (data) {
console.log(data)
},
function (err) {
console.log(err)
}
)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function co(gen) {
let it = gen()
return new Promise(function (resolve, reject) {
!function next(lastVal) {
let { value, done } = it.next(lastVal)
if (done) {
resolve(value)
} else {
value.then(next, (reason) => reject(reason))
}
}
})
}
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
# 2.6 Async/await
使用async
关键字,你可以轻松地达成之前使用生成器和 co 函数所做到的工作
# 6.5.1.Async 的优点
- 内置执行器
- 更好的语义
- 更广的适用性
let fs = require("fs")
function readFile(filename) {
return new Promise(function (resolve, reject) {
fs.readFile(filename, "utf8", function (err, data) {
if (err) {
reject(err)
} else {
resolve(data)
}
})
})
}
async function read() {
let template = await readFile("./tempalte.txt")
let data = await readFile("./data.txt")
return tempalte + "+" + data
}
let result = read()
result.then((data) => console.log(data))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 6.5.2.async 函数的实现
async 函数的实现,就是将 Generator 函数和自动执行器,包装在一个函数里
async function read() {
let template = await readFile("./template.txt")
let data = await readFile("./data.txt")
return template + "+" + data
}
// 等同于
function read() {
return co(function* () {
let template = yield readFile("./template.txt")
let data = yield readFile("./data.txt")
return template + "+" + data
})
}
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13