Web培训
400-996-5531
1、回调
回调简单地理解为一个函数作为参数传递给另一个函数,回调是早期最常用的异步解决方案之一。
回调不一定是异步的,也不直接相关。
举个简单的例子:
functionf1(cb){
setTimeout(()=>;
{
cb&;&;cb();
},
2000);
}
f1(()=>;
{console.log("1");
});
如上,我们在函数f1中使用setTimeout模拟一个耗时2s的任务,在耗时任务结束时抛出回调,这样我们就可以调用它,让回调函数在耗时结束时执行函数f1中的任务。
这样,我们就把同步操作变成了异步操作。f1不会阻塞程序,相当于先执行程序的主要逻辑,推迟执行耗时操作。
回调的优点和缺点
优点:简单,容易理解。
缺点:代码不优雅,可读性差,不易维护,耦合度高,层层嵌套造成回调地狱。
2、事件监听(发布订阅模式)
发布-订阅模式定义了对象之间一对多的依赖关系,这样当一个对象的状态发生变化时,所有依赖它的对象都会得到通知。
我们都使用过发布-订阅模式,例如,如果我们将事件函数绑定到DOM节点。
document.body.addEventListener('click',function(){
console.log('click');
})
但这只是发布-订阅模式最简单的使用,在很多场景下我们往往会使用一些自定义事件来满足我们的需求。
有很多方法可以实现发布-订阅模式,所以这里有一个使用类的简单实现。
classEmitter{constructor(){
//_listenerarray,keyisthecustomeventname,valueistheexecutioncallbackarray-astheremaybemorethanonethis._listener=[]}//订阅监听事件on(type,fn){
//Determineiftheeventexistsinthe_listenerarray.//Existstopushthecallbacktothevaluearraycorrespondingtotheeventname,doesnotexisttoadddirectlythis._listener[type]?this._listener[type].push(fn):(this._listener[type]=[fn])}
//PublishTriggerEventtrigger(type,...rest){
//Determineifthetriggereventexistsif(!this._listener[type])return
//Iteratethroughthearrayofcallbacksexecutingtheeventandpasstheparametersthis._listener[type].forEach(callback=>;callback(...rest))
}
}
如上所示,我们创建了一个Emitter类,并在和触发器上添加了两个原型方法,使用如下。
//Createanemitterinstanceconstemitter=newEmitter()emitter.on("done",function(arg1,arg2){
console.log(arg1,arg2)
})
emitter.on("done",function(arg1,arg2){
console.log(arg2,arg1)
})
functionfn1(){console.log('Iamthemainprogram')setTimeout(()=>;
{
emitter.trigger("done","AsynchronousparameterI","AsynchronousparameterII")},1000)}
fn1()
我们先创建一个emitter实例,然后注册事件,然后触发事件,这样也解决了异步问题。
事件监听的优点和缺点
优点:更符合模块化思想,我们在编写自己的监听器的时候可以做很多优化,从而更好的监听程序的运行。
缺点:整个程序变成了事件驱动,或多或少影响了流程,而且每次使用都要注册事件监听器然后触发,比较麻烦,代码也不是很优雅。
3、Promise
ES6标准化并引入了Promise对象,这是一种异步编程的解决方案。
简单的说,就是用同步的方式写异步代码,可以用来解决回调地狱问题。
Promise对象的状态一旦改变,就不会再改变,只有两种可能的改变。
1)由待定改为已解决。
2)由Pending改为Rejected。
我们使用setTimeout来模拟异步操作。
functionanalogAsync(n){
returnnewPromise((resolve)=>;
{setTimeout(()=>;resolve(n+500),n);
});
}functionfn1(n){
console.log(`step1with${n}`);
returnanalogAsync(n);
}functionfn2(n){
console.log(`step2with${n}`);
returnanalogAsync(n);
}functionfn3(n){
console.log(`step3with${n}`);
returnanalogAsync(n);
}
使用Promise来实现。
functionfn(){
lettime1=0;
fn1(time1).then((time2)=>;
fn2(time2)).then((time3)=>;
fn3(time3)).then((res)=>;
{
console.log(`resultis${res}`);
});}
fn(
);
Promise优点和缺点
优点:Promise以同步的方式编写异步代码,避免了回调函数层层嵌套,可读性更强。链式操作,可以在then中继续写Promise对象并return,然后继续调用then进行回调操作。
缺点:Promise对象一旦创建就会立即执行,不能中途取消。如果没有设置回调函数,Promise会在内部抛出错误,不会向外流。
4、Generator
Generator其实就是一个函数,只不过是一个特殊的函数。Generator的特别之处在于它可以中途停止。
function*generatorFn(){
console.log("a");
yield'1';console.log("b");
yield'2';console.log("c");
return'3';
}
letit=generatorFn();
it.next();
it.next();
it.next();
it.next();
上面的示例是一个具有以下特征的生成器函数。与普通函数不同,Generator函数在函数之后和函数名称之前有一个*,该函数有一个内部yield字段,函数调用后的返回值使用next方法。
Generator的优点和缺点
优点:优雅的流程控制方法,允许函数被中断地执行。
缺点:Generator函数的执行必须依赖executor,对于只做异步处理还是不太方便。
5、async/await
ES2017标准引入了async函数,使得异步操作更加方便。async是异步的意思,await是asyncwait的简写,也就是异步等待。async/await被许多人认为是js中异步操作的终极和最优雅的解决方案。
异步在做什么?
async函数返回一个Promise对象。如果直接在async函数中返回一个直接量,async会通过Promise.resolve()将直接量包装在一个Promise对象中。
await是什么?
await是一个表达式,其计算结果为Promise对象或其他值(换句话说,没有特殊限定,无论如何)。
如果await后面没有跟Promise对象,则直接执行。
如果await后面跟着一个Promise对象,它会阻塞后面的代码,Promise对象解析,然后获取resolve的值作为await表达式的结果。
await只能在异步函数中使用
上面使用setTimeout来模拟异步操作,我们使用async/await来实现。
asyncfunctionfn(){
lettime1=0;
lettime2=awaitfn1(time1);
lettime3=awaitfn2(time2);
letres=awaitfn3(time3);
console.log(`resultis${res}`);
}
fn();
输出结果和上面的Promise实现是一样的,但是async/await的代码结构看起来更清晰,几乎和同步写法一样优雅。
async/await的优点和缺点
优点:内置执行器,语义更好,适用性更广。
缺点:误用await可能会导致性能问题,因为await会阻塞代码。
填写下面表单即可预约申请免费试听! 怕学不会?助教全程陪读,随时解惑!担心就业?一地学习,可全国推荐就业!
Copyright © 京ICP备08000853号-56 京公网安备 11010802029508号 达内时代科技集团有限公司 版权所有
Tedu.cn All Rights Reserved