1. 阻塞与非阻塞IO
IO即为输入(input)和输出(output)
阻塞与非阻塞的区别在于输入后,输入方能不能去做其他的事情,如果不能则为阻塞,反正则为非阻塞。
以点菜为例,如果在食堂点菜,点菜阿姨需要把饭打好后,才能处理下一个同学的点菜。而在餐厅,服务员下单后,将单传给厨房,自己则可以继续为其他顾客下单。
在这个例子中,把订单传给厨房作为输入,点菜阿姨因为自己既是下单也是打饭的,下单后需要处理完当前订单才能让下一个同学下单,因此是阻塞的。而在餐厅,服务员下单后传给厨房,剩下的事就不是自己的职责范围了,可以给其他顾客服务,因此是非阻塞的。
2. 异步编程之callback
调用interview方法的时候传递一个回调方法,interview执行结束的时候再调用这个回调方法,至于回调方法的参数第一个参数是err,第二个参数是返回结果都是约定俗成的
2.1 callback示例
interview(function (err, res) {
if (err) {
return console.log('cry')
}
console.log('smile')
console.log(res) // success
})
function interview(callback) {
setTimeout(() => {
num = Math.random()
console.log(num)
if (num > 0.1) {
callback(null, 'success')
} else {
callback(new Error('fail'))
}
}, 500)
}
2.2 捕获异常
按下面这样是捕获不到错误的,因为node的事件循环机制,setTimeout会开启一个事件,会是一个全新的调用栈, node会循环判断这个事件是否已结束(是否一直循环暂时还不清楚),抛出错误后系统会崩溃,不会被捕获到,如果把setTimeout去掉则可以捕获到
try {
interview(function (err, res) {
if (err) {
return console.log('cry')
}
console.log('smile')
console.log(res) // success
})
} catch(e) {
console.log('报错了') // 这样是抓取不到错误的
}
function interview(callback) {
setTimeout(() => {
num = Math.random()
console.log(num)
if (num > 0.4) {
callback(null, 'success')
} else {
throw new Error('fail')
}
}, 500)
}
2.3 回调地狱
这种回调写法虽然简单,但是会带来下面这种情况,多次嵌套会出现回调地狱,代码难以阅读
interview(function(err, res) {
if (err) {
return console.log('cry')
}
interview(function(err, res) {
if (err) {
return console.log('cry')
}
interview(function(err, res) {
if (err) {
return console.log('cry')
}
console.log('interview success, smile')
})
})
})
function interview(callback) {
setTimeout(() => {
num = Math.random()
console.log(num)
if (num > 0.4) {
callback(null, 'success')
} else {
callback(new Error('fail'))
}
}, 500)
}
3. 异步编程之Promise
在promise还没有执行结果的时候是pending状态,执行成功后是resolved状态,执行失败是rejected状态,resovle和reject都只能接收一个参数
3.1 promise示例
(function() {
var promise = new Promise(function(resolve, reject) {
setTimeout(() => {
resolve(3);
}, 300)
}).then(function(res) {
console.log(res) // 3
}).catch(function(err) {
console.log(err)
})
console.log(promise) // 打印状态,在浏览器Console中调试能看到状态的变化
})()
在浏览器中调试,查看promise的状态
3.2 promise解决callback回调地狱的问题
var promise = interview(1)
.then((res) => {
return interview(2)
})
.then(() => {
return interview(3)
})
.then(() => {
console.log('三轮面试都成功了')
})
.catch((err) => {
// reject后的异常会被第一个catch捕获到
console.log('第' + err.round + '轮面试失败了')
})
function interview(round) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (Math.random() > 0.4) {
resolve('success')
} else {
var error = new Error('fail')
error.round = round
reject(error)
}
}, 500)
})
}
3.3 promise异步任务并行执行
Promise
.all([
interview(1),
interview(2)
])
.then(() => {
console.log('两个并行任务都执行成功了')
})
.catch((err) => {
console.log('某个任务出现了问题')
})
4. 异步编程之async-await
await会等待后面的promise执行结束,从而达到同步的方式写异步
4.1 async-await示例
await后面跟着的就是promise
(async function() {
try {
await interview(1)
await interview(2)
await interview(3)
} catch(err) {
return console.log('第' + err.round + '轮面试失败了')
}
console.log('面试成功')
})()
function interview(round) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (Math.random() > 0.4) {
resolve('success')
} else {
var error = new Error('fail')
error.round = round
reject(error)
}
}, 100)
})
}
4.1 异步任务并行执行
(async function() {
try {
await Promise.all([interview(1), interview(2)])
} catch(err) {
return console.log('第' + err.round + '轮面试失败了')
}
console.log('面试都成功了')
})()