异步流程解决方案
一、同步与异步
我们可以通俗理解为异步就是一个任务分成两段,先执行第一段,然后转而执行其他任务,等做好了准备,再回过头执行第二段。排在异步任务后面的代码,不用等待异步任务结束会马上运行,也就是说,异步任务不具有”堵塞“效应。比如,有一个任务是读取文件进行处理,异步的执行过程就是下面这样
这种不连续的执行,就叫做异步。相应地,连续的执行,就叫做同步
"异步模式"非常重要。在浏览器端,耗时很长的操作都应该异步执行,避免浏览器失去响应,最好的例子就是Ajax操作。在服务器端,"异步模式"甚至是唯一的模式,因为执行环境是单线程的,如果允许同步执行所有http请求,服务器性能会急剧下降,很快就会失去响应。接下来介绍下异步编程四种方法。
- 回调函数实现
- 事件监听
- Promise/A
- async/await
二、回调
所谓回调函数,就是把任务的第二段单独写在一个函数里面,等到重新执行这个任务的时候,就直接调用这个函数
fs.readFile('某个文件', function (err, data) {
if (err) throw err;
console.log(data);
});
这是一个错误优先的回调函数(error-first callbacks),这也是Node.js本身的特点之一。
1.回调的问题
异步代码时try catch不再生效
let async = function(callback){
try{
setTimeout(function(){
callback();
},1000)
}catch(e){
console.log('捕获错误',e);
}
}
async(function(){
console.log(t);
});
因为这个回调函数被存放了起来,直到下一个事件环的时候才会取出,try只能捕获当前循环内的异常,对callback异步无能为力
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);
})
})
三、事件监听
这种方式下,异步任务的执行不取决于代码的顺序,而取决于某个事件是否发生。
下面是两个函数f1和f2,编程的意图是f2必须等到f1执行完成,才能执行。首先,为f1绑定一个事件(这里采用的jQuery的写法)
f1.on('done', f2);
上面这行代码的意思是,当f1发生done事件,就执行f2。然后,对f1进行改写:
function f1() {
setTimeout(function () {
// ...
f1.trigger('done');
}, 1000);
}
上面代码中,f1.trigger(‘done’)表示,执行完成后,立即触发done事件,从而开始执行f2。
这种方法的优点是比较容易理解,可以绑定多个事件,每个事件可以指定多个回调函数,而且可以"去耦合",有利于实现模块化。缺点是整个程序都要变成事件驱动型,运行流程会变得很不清晰。阅读代码的时候,很难看出主流程。
四、Promise/A+
Promise本意是承诺,在程序中的意思就是承诺我过一段时间后会给你一个结果。 什么时候会用到过一段时间?答案是异步操作,异步是指可能比较长时间才有结果的才做,例如网络请求、读取本地文件等
1.Promise的三种状态
- Pending----Promise对象实例创建时候的初始状态
- Fulfilled----可以理解为成功的状态
- Rejected----可以理解为失败的状态
这个承诺一旦从等待状态变成为其他状态就永远不能更改状态了,比如说一旦状态变为 resolved 后,就不能再次改变为Fulfilled
let p = new Promise((resolve, reject) => {
reject('reject')
resolve('success')//无效代码不会执行
})
p.then(
value => {
console.log(value)
},
reason => {
console.log(reason)//reject
}
)
当我们在构造 Promise 的时候,构造函数内部的代码是立即执行的
new Promise((resolve, reject) => {
console.log('new Promise')
resolve('success')
})
console.log('end')
// new Promise => end
五、Async/ await
使用async关键字,你可以轻松地达成之前使用生成器和co函数所做到的工作
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('./template.txt');
let data = await readFile('./data.txt');
return template + '+' + data;
}
let result = read();
result.then(data=>console.log(data));
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;
});
}
共同学习,写下你的评论
评论加载中...
作者其他优质文章