管中窥豹——JS中异步的实现

什么是异步

在了解异步的的前面我们要了解一个东西。javascript运行机制是单线程,即一般情况下都是一个个的执行,如果遇到多种情况一起来,只能排队。这种就叫同步

这样就会导致一个情况。如果前者的任务的执行时间特别长,导致后面某些任务无法执行。造成页面假死。这种体验一般来说都很糟糕。这样就需要用到异步

最简单的异步模式

js对于异步的处理,很多人的第一反应是ajax,这只能说是对了一半。我感觉setTimeoutsetInterval算是最早的js实现异步模式了。

这里举个简单的例子一笔带过

1
2
3
4
5
6
7
8
9
10
11
12
13
14

let zone=()=>{
console.log(1)

setTimeout(()=>{
console.log(2)
},1000)

setTimeout(()=>{
console.log(3)
},0)
console.log(4)
}
zone()

image
结果很显而易见。这里不做过多说明

不过这里说一下setTimeout不带参数和参数为0的区别。

  • 参数为0,其实就相当于一个callback回调函数。相当于插队,等上一个程序执行完,就立刻执行这个程序。
  • 不带参数。一般就是放置在末尾最后处理

优点:

  • 最简单的回调

缺点

  • 容易造成回调地狱。
  • 比较Low

Promise

看过ES6的基本上都知道这个东西了。差不多就是专门用于解决异步相关的问题,两个方法thencatchthen方法第一个参数是resolve状态时执行的回调,第二个参数则是reject状态时执行的回调,而catch则是then中有一环是reject就执行的回调函数。三个状态pending(进行中), fulfilled(已完成), rejected(已失败)

一个简单的promise

1
2
3
4
5
6
7
var promise=new Promise(function(resove,reject){
if (/* 异步操作成功 */){
resolve(value);
} else {
reject(error);
}
})

Promise实例生成以后,可以用then方法分别指定resolved状态和reject状态的回调函数。

比如这个例子

1
2
3
4
5
6
7
8
9
10
let promise=new Promise(function(resolve,reject){
console.log('说点什么好呢');
setTimeout(()=>{
resolve();
},1000)
})
promise.then(function(){
console.log('我后执行')
})
console.log('三大赵日天')

image
这里结果很明显,第一次声明promise对象的时候,会执行一遍。
所以”说点什么好呢”第一,然后输出”三大赵日天”,一秒之后 promise的状态由pending变为resolve出发then之后的回调。一些复杂的用法可以去看看阮一峰的介绍。这里我也记不清楚。

ES8的async

其实promise已经足够我们应付一些简单的业务需求的了。但是面对多重调用的promise方法,也会出现一些问题。例如

1
2
3
4
5
6
7
8
9
10

promise.then(db=> {
return promiseB(db);
}).then(coll=> {
return promiseC(coll);
}).then(blogs=> {
console.log(blogs;
}).catch(err=> {
console.log(err);
})

虽然在这个Promise链上,如果任何一个环节出现异常,都会被最后的catch()捕捉到。比起层层回调会显得稍微好点,但是有一点不是很好,这里的then()方法获取的对象,都是上一个方法返回的对象。这样而且还不能跨层访问。

这时候就需要用到async

还是直接上代码举个例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var sleep = function (time) {
return new Promise(function (resolve, reject) {
setTimeout(function () {
resolve();
}, time);
})
};

var zone=sleep(2000).then(()=>{
console.log("三大赵日天");
return sleep(2000)
}).then(()=>{
console.log("三大赵日天")
return sleep(2000)
}).then(()=>{
console.log("三大赵日天")
})

这样基本上就实现了 每隔两秒输出一次"三大赵日天"
显然这样是不够优雅的。虽然不Low

如果用async会怎么样呢?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var sleep = function (time) {
return new Promise(function (resolve, reject) {
setTimeout(function () {
resolve();
}, time);
})
};

var zone=async function(){
console.log("开始了")
await sleep(2000);
console.log("三大赵日天")
await sleep(2000)
console.log("三大赵日天")
}

zone();

image
其实 async感觉就是promise的拓展版,用了同步的实现去实现异步。只不过async 没有thencatch,捕获异常是用的try catch。实现起来也很简单。
这里对上面代码做点加工

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var sleep = function (time) {
return new Promise(function (resolve, reject) {
setTimeout(function () {
reject("出错啦");
}, time);
})
};

var zone=async function(){
try{
console.log("开始了")
await sleep(2000);
console.log("三大赵日天")
await sleep(2000)
console.log("三大赵日天")
} catch(res){
console.log("出错了",res)
}
}
zone();

image


基本上常见的JS实现异步的方法。上面几种平时处理一般业务逻辑也够用了。感觉平时学习预期多背框架API 还不如抽时间弄明白一些基础的原理。这样后面学习新的框架的时候也能很快适应。。有些原理弄明白之后,框架自然也就熟悉API了。去年有人问过我promise 那时候还只会背背API。照着别人模子套。这段时间看async查了查资料 总算能明白点大概 也算有点进步了。。