JS异步流程控制

单线程

就是指一次只能完成一件任务。如果有多个任务,就必须排队,前面一个任务完成,再执行后面一个任务,以此类推。特点:与其余线程无法共享状态

优点:

  1. 不用像多线程那样处处在意同步的问题
  2. 这里没有死锁的存在
  3. 没有线程切换带来的性能开销

    缺点:

  4. 无法利用多核CPU
  5. 错误会引起整个应用的退出,健壮性值得考虑
  6. 大量计算无法继续利用CPU,无法继续异步调用异步I/O
  7. 只要有一个任务耗时很长,后面的任务都必须排队等着,会拖延整个程序的执行。

为了解决这个问题,Javascript语言将任务的执行模式分成两种:同步(Synchronous)和异步(Asynchronous)。

同步

后一个任务等待前一个任务结束,然后再执行,程序的执行顺序与任务的排列顺序是一致的、同步的;

异步

每一个任务有一个或多个回调函数(callback),前一个任务结束后,不是执行后一个任务,而是执行回调函数,后一个任务则是不等前一个任务结束就执行,所以程序的执行顺序与任务的排列顺序是不一致的、异步的。

粗糙的理解为:男朋友和女朋友打算结婚,中间插入了一个小三。等小三的勾搭任务完成之后,男女朋友的结婚任务还是得继续=.=

如何实现异步?

回调(Express博客的首页谈起)

需求:当浏览器请求我的博客首页时,里面的内容是包括了前端文章和留言的两个表。开始玩的时候我会这样写:

函数被作为参数进行传递

1
2
3
4
5
6
7
8
9
10
app.get('/index', function(req, res) {
FE.findBylimit(5, function(err, fes, next) { // 前端的文章列表查询
MESSAGE.findByNum(1, function(err, MES) { // 利用回调进行留言列表的查询,尴尬QAQ
res.render("index", {
fes: fes,
MES: MES,
})
})
})
});

这个就是陷入了著名的回调深渊。当回调过多,我们的回调金字塔会变成这样:

1
2
3
4
5
6
7
8
9
step1(function (value1) {
step2(value1, function(value2) {
step3(value2, function(value3) {
step4(value3, function(value4) {
// Do something with value4
});
});
});
});

Generator(Koa重构博客)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
app.use(route.get('/', getInfo));

function* getInfo() {
let fes = getInfo(5);
let msg = getMsg(nums)
this.body = yield render('list', { infos: { fes: fes, msg: msg } });
}

function* getFE(num) {
return yield FE.findBylimit(num); //只查询前num条数据
}

function* getMsg(num) {
return yield MESSAGE.findByNum(1); //分页查询,查询第一页数据
}

知其所以然(Generator的原理)

1
2
3
4
5
6
7
8
9
10
11
function* hiGenerator() {
yield 'hi';
yield 'ES5';
return '!';

}
var hi = hiGenerator();
console.log(hi); //hiGenerator {[[GeneratorStatus]]: "suspended", [[GeneratorReceiver]]: Window}
console.log(hi.next()); //Object {value: "hi", done: false}
console.log(hi.next()); //Object {value: "ES5", done: false}
console.log(hi.next()); //Object {value: "!", done: true}

async/await

1
2
3
4
5
6
7
async function hiGenerator() {
await h();
await ES();
}
let h = () => { console.log('Hi') }
let ES = () => { console.log('ES5') }
hiGenerator();