异步: 所谓异步,简单来说就是 不能连续执行,上一个任务没运行完,下一个任务照样运行,任务之间不能连续,不能首尾相连。
同步: 同步则与异步相反,是 连续执行 的,下一个任务紧接着上一个任务之后运行,如果上一个任务没有运行完,下一个任务没法运行。
当我们需要在执行的函数中进行其他的操作,就需要用到回调函数了。如:
function fn(args, callback){
//TODO...
callback();
}
fn(args, ()=> {
//TODO...
})
如DOM事件的监听、鼠标键盘的事件监听等,都是异步执行的。
document.addEventListener("click", (e)=>{
//TODO...
}, false)
比如我要开一家新公司,供用户加入,当我在新公司发布消息时,只有注册用户才能收到消息。
我们架构程序时,我们可以从使用出发,比如先写我们要怎么用,如:
// 首先要开新公司
const company = new Company();
// 然后需要有些用户吧,比如用户A、B等等
// 用户A
const userA = function(msg){
console.log(`用户A接收到的消息为:${msg}`)
}
// 用户B
const userB = function(msg){
console.log(`用户B接收到的消息为:${msg}`)
}
// 再接着把这些用户注册到新公司里(他们就是这公司的注册用户了)
company.addSubscribe(userA, userB);
// 这样我就可以以公司的名义向用户发送消息了吧
company.release('今天公司成立,注册用户每人奖励一万元整。');
然后接下来我们是不是要考虑怎么实现这个Company啦(就是注册这公司有些什么样的手续及后续的功能)。如:
// 首先我们是不是要定义一个这样的类啊
class Company {
constructor(){
// 在类里面我们要定义一个存储用户数据的变量
this.users = [];
}
// 前面我们用到的添加用户的功能
addSubscribe(...args){
args.forEach((val) => {
if(!this.users.includes(val)){
this.users.push(val);
}
})
return this;
}
// 还需要发布消息的功能吧
release(msg){
this.users.forEach((fn) => {
fn(msg);
})
return this;
}
}
es6新增的异步处理,如果按第1项里的回调函数,如果多层调用会很麻烦,如ajax:
// 比如我们要先获取token,再根据token去获取用户信息,然后根据用户信息获取其他的内容等等
// 就得这样(这样一直嵌套感觉很不爽)
ajax(option, (res) => {
// 拿到token
ajax(option, (res) => {
// 拿到用户信息
ajax(option, (res) => {
// 拿到其他等等数据
})
})
})
那么在es6里面我们可以怎么做呢,Promise的语法什么的我就不说了,我只讲思路。
如:
// 封装一个ajax方法
function myAjax(option){
return new Promise((resolve, reject) => {
ajax(option, (res) => {
resolve(res);
}, (err) => {
reject(err);
})
});
}
// 此时我用这个方法去完成上面的需求
myAjax(option).then(res => {
// 拿到token
return myAjax(res.token);
}).then(res => {
// 拿到用户信息
return myAjax(res.info);
}).then(res => {
// 拿到其他等等数据
console.log(res);
})
简单来说,她是个可控制运行的函数,函数体内的内容是可控的。你叫她往下运行她就往下运行。来看一个简单的例子:
function* idMaker(){
let index = 0;
while(true)
yield index++;
}
let gen = idMaker(); // 返回 generator 对象
// gen.next()方法,返回 yield 表达式产生的值
console.log(gen.next().value); // 0
console.log(gen.next().value); // 1
console.log(gen.next().value); // 2
// ...
有人就会问了,平时咋用啊,貌似也用不到吧。那我们就结合上面Ajax,来实现一个类似需求,我们经常会遇到在一个内容很多的页面上,我们是不是需要先加载什么后加载什么呢,比如我们得先把菜单列表的数据请求过来,再去请求新闻啊、活动啊之类的,对吧。那么我们来看看怎么玩。
// 这里我们直接用上面封装的myAjax方法
// 首先我们要定义一个generator函数
function* gen(){
yield myAjax(option); // 请求菜单
yield myAjax(option); // 请求新闻
yield myAjax(option); // 请求活动
}
let g = gen();
g.next().value.then(res => {
console.log('菜单请求成功');
return g.next().value; // 菜单请求成功之后,接着往下请求
}).then(res => {
console.log('新闻请求成功');
return g.next().value; // 新闻请求成功之后,接着往下请求
}).then(res => {
console.log('活动请求成功');
})
// 看完是不是感觉跟前面的Promise一样啊,对,没错,就是一样的。唯一不同的是什么呢,我们把请求的参数放上一块了(option放到了gen函数里面了),这样就方便代码管理啦。
但是呢,每次都要手机调用,也很麻烦,再说了,generator语言化也不怎么友好,比如“*”号啥意思?“yield”关键字又是啥意思?感觉不符合场景。有更好的东东吗?当然有,请接着往下看!
没错,就是她了,字面意思已经告诉你了 “async”异步的意思,“await”等待。那她怎么用呢,看例子:
// 这里我们还是直接用上面封装的myAjax方法
// 定义一个异步函数
async function fn(){
let token = await myAjax(option); // 拿到token
let info = await myAjax(token); // 根据token去拿用户信息.
let other = await myAjax(info); // 根据info去拿其他信息
}
// 最后我们再调用这个函数
fn() // async函数,返回的是一个Promise对象,你看哪儿哪儿都有Promise,是不是很重要
// 那么既然是一个Promise对象,可不可以.then 或 .catch啊,当然可以
async function fn2(){
return 'allen'
}
fn2().then(res => {
console.log(res); // allen
})
// 这样是不是方便很多,语义化也很好啊。
这里有几个点需要注意:
1、await只能放在async函数中。
2、await后面可以是任何对象
3、async函数返回的是一个Promise对象
4、如果await后面的Promise状态变为reject,那么会停止整个async函数
总的来说,以后常用的估计就是Promise与async函数、await的配合使用了,当然我们首先得了解Promise,了解异步。谢谢大家。
注:爱护原创,转载请说明出处