1.回调函数
function f1(callback){
setTimeout(function(){
//f1的任务代码
callback();
},1000);
}
f1(f2);
ajax('XXX1', () => {
// callback 函数体
ajax('XXX2', () => {
// callback 函数体
ajax('XXX3', () => {
// callback 函数体
})
})
})
优点:便于理解
缺点:回调地狱,不能捕获错误
2.事件监听
f1.on('done', f2);
function f1(){
setTimeout(function () {
// f1的任务代码
f1.trigger('done');
}, 1000);
}
容易理解,可以绑定多个事件,每个事件可以指定多个回调函数;缺点,整个流程都要变成事件驱动型,运行流程会变得不清晰。
3.发布/订阅(观察者模式)
jQuery.subscribe("done", f2);
function f1(){
setTimeout(function () {
// f1的任务代码
jQuery.publish("done");
}, 1000);
}
jQuery.unsubscribe("done", f2);
与"事件监听"类似,但是明显优于后者。因为我们可以通过查看"消息中心",了解存在多少信号、每个信号有多少订阅者,从而监控程序的运行。
4.Promises对象
Promise就是为了解决callback的问题而产生的
Promise实现了链式调用,也就是说每次then后返回的都是一个全新的Promise,如果我们在 then 中 return ,return 的结果会被 Promise.resolve() 包装
ajax('XXX1')
.then(res => {
// 操作逻辑
return ajax('XXX2')
}).then(res => {
// 操作逻辑
return ajax('XXX3')
}).then(res => {
// 操作逻辑
})
解决了回调地狱的问题,无法取消 Promise ,错误需要通过回调函数来捕获
5.Generator
特点:可以控制函数的执行,Generator函数是协程在ES6中的实现,最大的特点就是可以交出函数的执行权(即暂停执行)
执行Generator函数后会返回一个遍历器对象(Iterator)。本质上Generator是一个状态机,每次的遍历Generator函数返回的都是函数内部的每个状态。
function* gen() {
yield 1;
yield 2;
return 1;
}
var g = gen();
gen.next() // {value:1,done:false}
gen.next() // {value:2,done:false}
gen.next() // {value:3,done:true}
Generator函数是分段执行的,yield语句是暂停执行的标记,而next方法可以恢复执行。缺点还需要编写自动执行器
6.使用async函数
async函数的实现就是将Generator函数和自动执行器包装在一个函数中
function async gen(){
awiat 1;
awiat 2;
}
异步编程的语法目标,就是怎样让它更像同步编程,使用async/await的方法,使得异步编程与同步编程看起来相差无几了。
7.借助流程控制库
NPM社区中出现了很多流程控制库可以供开发者直接使用,其中很流行的就是async库,该库提供了一些流程控制方法,其官方文档见http://caolan.github.io/async/docs.html
如果需要执行的任务紧密结合。下一个任务需要上一个任务的结果做输入,应该使用瀑布式
如果多个任务必须依次执行,而且之间没有数据交换,应该使用串行执行
如果多个任务之间没有任何依赖,而且执行顺序没有要求,应该使用并行执行
8.使用Web Workers
Web Worker是HTML5新标准中新添加的一个功能,Web Worker的基本原理就是在当前javascript的主线程中,使用Worker类加载一个javascript文件来开辟一个新的线程,起到互不阻塞执行的效果,并且提供主线程和新线程之间数据交换的接口:postMessage,onmessage。其数据交互过程也类似于事件发布/监听模式,异能实现异步操作
9 对比
异步读取文件
1.回调函数
fs.readFile(fileA,(err,data)=>{
if(err) throw err;
console.log(data);
fs.readFile(fileB,(_err,_data)=>{
if(_err) throw err;
console.log(_data)
})
}
2.使用Promise
var fs = require('fs');
var readFile = function(path) {
return new Promise((resolve, reject) => {
fs.readFile(path, (err, data) => {
if (err) reject(err)
resolve(data)
})
})
}
readFile(fileA)
.then((data)=>{console.log(data)})
.then(()=>{return readFile(fileB)})
.then((data)=>{console.log(data)})
// ... 读取n次
.catch((err)=>{console.log(err)})
3.使用Generator
var fs = require('fs');
var readFile = (path) => {
return new Promise((resolve, reject) => {
fs.readFile(path, (err, data) => {
if (err) reject(err)
resolve(data)
})
})
}
var gen = function* () {
var f1 = yield readFile(fileA);
var f2 = yield readFile(fileB);
console.log(f1.toString());
console.log(f2.toString());
}
gen.next()
grn.next()
4.使用Async
var asyncReadFile = async function() {
var f1 = await readFile(fileA);
var f2 = await readFile(fileB);
console.log(f1.toString())
console.log(f2.toString())
}
网友评论