前端异步(async)解决方案(所有方案)(一)

简介: 前端异步(async)解决方案(所有方案)

javascript是一门单线程语言,即一次只能完成一个任务,若有多个任务要执行,则必须排队按照队列来执行(前一个任务完成,再执行下一个任务)。


这种模式执行简单,但随着日后的需求,事务,请求增多,这种单线程模式执行效率必定低下。只要有一个任务执行消耗了很长时间,在这个时间里后面的任务就无法执行。常见的浏览器无响应(假死),往往就是因为某一段Javascript代码长时间运行(比如死循环),导致整个页面卡在这个地方,其他任务无法执行。(弊端)


为了解决这个问题,javascript语言将任务执行模式分成同步和异步:


同步模式:就是上面所说的一种执行模式,后一个任务等待前一个任务结束,然后再执行,程序的执行顺序与任务的排列顺序是一致的、同步的。


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


20210717142007423.jpg

“异步模式”非常重要。在浏览器端,耗时很长的操作都应该异步执行,避免浏览器失去响应,最好的例子就是Ajax操作。在服务器端,”异步模式”甚至是唯一的模式,因为执行环境是单线程的,如果允许同步执行所有http请求,服务器性能会急剧下降,很快就会失去响应。(异步模式的重要性)


下面就带来几种前端异步解决方案:


一.传统方案

1.回调函数(callback):

异步编程的基本方法。

首先需要声明,回调函数只是一种实现,并不是异步模式特有的实现。回调函数同样可以运用到同步(阻塞)的场景下以及其他一些场景。


回调函数的定义:


函数A作为参数(函数引用)传递到另一个函数B中,并且这个函数B执行函数A。我们就说函数A叫做回调函数。如果没有名称(函数表达式),就叫做匿名回调函数。


生活举例:约会结束后你送你女朋友回家,离别时,你肯定会说:“到家了给我发条信息,我很担心你。” 然后你女朋友回家以后还真给你发了条信息。其实这就是一个回调的过程。你留了个参数函数(要求女朋友给你发条信息)给你女朋友,然后你女朋友回家,回家的动作是主函数。她必须先回到家以后,主函数执行完了,再执行传进去的函数,然后你就收到一条信息了。


案例:

//定义主函数,回调函数作为参数functionA(callback) {
callback();  
console.log('我是主函数');      
}
//定义回调函数functionB(){
setTimeout("console.log('我是回调函数')", 3000);//模仿耗时操作  }
//调用主函数,将函数B传进去A(B);
//输出结果我是主函数我是回调函数

上面的代码中,我们先定义了主函数和回调函数,然后再去调用主函数,将回调函数传进去。


定义主函数的时候,我们让代码先去执行callback()回调函数,但输出结果却是后输出回调函数的内容。这就说明了主函数不用等待回调函数执行完,可以接着执行自己的代码。所以一般回调函数都用在耗时操作上面。比如ajax请求,比如处理文件等。


优点:简单,容易理解和 部署。


缺点:不利于代码的阅读,和维护,各部分之间高度耦合,流程会很混乱,而且每一个任务只能指定一个回调函数。


2.事件监听


采用事件驱动模式。


任务的执行不取决代码的顺序,而取决于某一个事件是否发生。


监听函数有:on,bind,listen,addEventListener,observe


以f1和f2为例。首先,为f1绑定一个事件(采用jquery写法)。

f1.on('done',f2);


上面代码意思是,当f1发生done事件,就执行f2。

然后对f1进行改写:

functionf1(){
settimeout(function(){
//f1的任务代码f1.trigger('done');  
   },1000);
}

f1.trigger('done')表示,执行完成后,立即触发done事件,从而开始执行f2.

优点:比较容易理解,可以绑定多个事件,每一个事件可以指定多个回调函数,而且可以去耦合,有利于实现模块化。

缺点:整个程序都要变成事件驱动型,运行流程会变得不清晰。

事件鉴定方法:

(1).onclick方法:

element.onclick=function(){
   //处理函数
}

优点:写法兼容到主流浏览器。

缺点:当同一个element元素绑定多个事件时,只有最后一个事件会被添加。

例如:

element.onclick=handler1;
element.onclick=handler2;
element.onclick=handler3;

上诉只有handler3会被添加执行,所以我们使用另外一种方法添加事件。(2)attachEvent和addEvenListener方法

(2).attachEvent和addEvenListener方法:

//IE:attachEvent(IE下的事件监听)elment.attachEvent("onclick",handler1);
elment.attachEvent("onclick",handler2);
elment.attachEvent("onclick",handler3);

上述三个方法执行顺序:3-2-1;

//标准addEventListener(标准下的监听)elment.addEvenListener("click",handler1,false);
elment.addEvenListener("click",handler2,false);
elment.addEvenListener("click",handler3,false);>

执行顺序:1-2-3;

PS:该方法的第三个参数是冒泡获取(useCapture),是一个布尔值:当为false时表示由里向外(事件冒泡),true表示由外向里(事件捕获)。

<div id="id1">
    <div id="id2"></div>
</div>

document.getElementById("id1").addEventListener("click",function(){
console.log('id1');
},false);
document.getElementById("id2").addEventListener("click",function({
console.log('id2');
},false);
//点击id=id2的div,先在console中输出,先输出id2,在输出id1document.getElementById("id1").addEventListener("click",function({
console.log('id1');
},false);
document.getElementById("id2").addEventListener("click",function({
console.log('id2');
},true);
//点击id=id2的div,先在console中输出,先输出id1,在输出id2

(3).DOM方法addEventListener()和removeListenner():


addEventListenner()和removeListenner()表示用来分配和删除事件的函数。这两种方法都需要三种参数,分别为:string(事件名称),要触发事件的函数function,指定事件的处理函数的时期或者阶段(boolean)。例子见(2)


(4).通用的时间添加方法:


on:function(elment,type,handler){
//添加事件returnelement.attachEvent?elment.attachEvent("on"+type,handler):elment.addEventListener(type,handler,false);
}

事件冒泡和事件捕获的区别,可以参考:

二.工具方案

工具方案大致分为以下5个:


Promise

gengerator函数

async await

node.js中 nextTick setImmidate

第三方库 async.js

下面针对每一个做详细说明应用:


1.Promise(重点)


(1).Promise的含义和发展:


含义:Promise 对象用于一个异步操作的最终完成(或失败)及其结果值的表示。简单点说,它就是用于处理异步操作的,异步处理成功了就执行成功的操作,异步处理失败了就捕获错误或者停止后续操作。


发展:Promise 是异步编程的一种解决方案,比传统的解决方案–回调函数和事件--更合理和更强大。它由社区最早提出和实现,ES6将其写进了语言标准,统一了语法,原生提供了Promise


(2).它的一般形式:

newPromise(
/* executor */function(resolve, reject) {
if (/* success */) {
// ...执行代码resolve();
        } else { /* fail */// ...执行代码reject();
        }
    }
);

其中,Promise中的参数executor是一个执行器函数,它有两个参数resolve和reject。它内部通常有一些异步操作,如果异步操作成功,则可以调用resolve()来将该实例的状态置为fulfilled,即已完成的,如果一旦失败,可以调用reject()来将该实例的状态置为rejected,即失败的。


我们可以把Promise对象看成是一条工厂的流水线,对于流水线来说,从它的工作职能上看,它只有三种状态,一个是初始状态(刚开机的时候),一个是加工产品成功,一个是加工产品失败(出现了某些故障)。同样对于Promise对象来说,它也有三种状态:pending: 初始状态,也称为未定状态,就是初始化Promise时,调用executor执行器函数后的状态。 fulfilled:完成状态,意味着异步操作成功。


pending:初始状态,也称为未定状态,就是初始化Promise时,调用executor执行器函数后的状态。

fulfilled:完成状态,意味着异步操作成功。

rejected:失败状态,意味着异步操作失败。

它只有两种状态可以转化,即


操作成功:pending -> fulfilled

操作失败:pending -> rejected

注意:并且这个状态转化是单向的,不可逆转,已经确定的状态(fulfilled/rejected)无法转回初始状态(pending)。


(3).Promise对象的方法(api):


1):Promise.prototype.then(callback)


Promise对象含有then方法,then()调用后返回一个Promise对象,意味着实例化后的Promise对象可以进行链式调用,而且这个then()方法可以接收两个函数,一个是处理成功后的函数,一个是处理错误结果的函数。


如下:

varpromise1=newPromise(function(resolve, reject) {
// 2秒后置为接收状态setTimeout(function() {
resolve('success');
  }, 2000);
});
promise1.then(function(data) {
console.log(data); // success}, function(err) {
console.log(err); // 不执行}).then(function(data) {
// 上一步的then()方法没有返回值console.log('链式调用:'+data); // 链式调用:undefined }).then(function(data) {
// ....});

在这里我们主要关注promise1.then()方法调用后返回的Promise对象的状态,是pending还是fulfilled,或者是rejected?


返回的这个Promise对象的状态主要是根据promise1.then()方法返回的值,大致分为以下几种情况:


1.如果then()方法中返回了一个参数值,那么返回的Promise将会变成接收状态。


2.如果then()方法中抛出了一个异常,那么返回的Promise将会变成拒绝状态。

3. 如果then()方法调用resolve()方法,那么返回的Promise将会变成接收状态。

4. 如果then()方法调用reject()方法,那么返回的Promise将会变成拒绝状态。

5.如果then()方法返回了一个未知状态(pending)的Promise新实例,那么返回的新Promise就是未知 状态。


6.如果then()方法没有明确指定的resolve(data)/reject(data)/return data时,那么返回的新Promise就是接收状态,可以一层一层地往下传递。


2):Promise.prototype.catch(callback)


catch()方法和then()方法一样,都会返回一个新的Promise对象,它主要用于捕获异步操作时出现的异常。因此,我们通常省略then()方法的第二个参数,把错误处理控制权转交给其后面的catch()函数,如下:

varpromise3=newPromise(function(resolve, reject) {
setTimeout(function() {
reject('reject');
  }, 2000);
});
promise3.then(function(data) {
console.log('这里是fulfilled状态'); // 这里不会触发// ...}).catch(function(err) {
// 最后的catch()方法可以捕获在这一条Promise链上的异常console.log('出错:'+err); // 出错:reject});
相关文章
|
15天前
|
前端开发 数据可视化 搜索推荐
深入剖析极态云优雅的前端框架设计方案(上)
最近在体验极态云,这款低代码软件开发产品,发现其前端框架设计方案很优雅很强大! 在接下来的学习过程中,我将持续输出自己对极态云前端框架设计方案的深入理解,包括具体的使用技巧、优势分析以及可能的应用场景等方面的内容,希望能为大家提供有价值的参考。
|
1月前
|
前端开发 JavaScript
乾坤qiankun(微前端)样式隔离解决方案--使用插件替换前缀
乾坤qiankun(微前端)样式隔离解决方案--使用插件替换前缀
290 8
|
1月前
|
设计模式 前端开发 JavaScript
前端编程的异步解决方案有哪些?
本文首发于微信公众号“前端徐徐”,介绍了异步编程的背景和几种常见方案,包括回调、事件监听、发布订阅、Promise、Generator、async/await和响应式编程。每种方案都有详细的例子和优缺点分析,帮助开发者根据具体需求选择最合适的异步编程方式。
69 1
|
30天前
|
缓存 前端开发 UED
前端 8 种图片加载优化方案梳理
本文首发于微信公众号“前端徐徐”,详细探讨了现代网页设计中图片加载速度优化的重要性及方法。内容涵盖图片格式选择(如JPEG、PNG、WebP等)、图片压缩技术、响应式图片、延迟加载、CDN使用、缓存控制、图像裁剪与缩放、Base64编码等前端图片优化策略,旨在帮助开发者提升网页性能和用户体验。
180 0
|
2月前
|
Web App开发 前端开发 JavaScript
Web前端项目的跨平台桌面客户端打包方案之——CEF框架
Chromium Embedded Framework (CEF) 是一个基于 Google Chromium 项目的开源 Web 浏览器控件,旨在为第三方应用提供嵌入式浏览器支持。CEF 隔离了底层 Chromium 和 Blink 的复杂性,提供了稳定的产品级 API。它支持 Windows、Linux 和 Mac 平台,不仅限于 C/C++ 接口,还支持多种语言。CEF 功能强大,性能优异,广泛应用于桌面端开发,如 QQ、微信、网易云音乐等。CEF 开源且采用 BSD 授权,商业友好,装机量已超 1 亿。此外,GitHub 项目 CefDetector 可帮助检测电脑中使用 CEF
320 3
|
3月前
|
缓存 前端开发 JavaScript
前端性能优化方案
【8月更文挑战第15天】前端性能优化方案
37 2
|
2月前
|
编解码 前端开发 JavaScript
前端移动端适配方案
【9月更文挑战第8天】前端移动端适配方案
93 0
|
3月前
|
开发者 安全 UED
JSF事件监听器:解锁动态界面的秘密武器,你真的知道如何驾驭它吗?
【8月更文挑战第31天】在构建动态用户界面时,事件监听器是实现组件间通信和响应用户操作的关键机制。JavaServer Faces (JSF) 提供了完整的事件模型,通过自定义事件监听器扩展组件行为。本文详细介绍如何在 JSF 应用中创建和使用事件监听器,提升应用的交互性和响应能力。
37 0
|
3月前
|
前端开发 JavaScript 中间件
【前端状态管理之道】React Context与Redux大对决:从原理到实践全面解析状态管理框架的选择与比较,帮你找到最适合的解决方案!
【8月更文挑战第31天】本文通过电子商务网站的具体案例,详细比较了React Context与Redux两种状态管理方案的优缺点。React Context作为轻量级API,适合小规模应用和少量状态共享,实现简单快捷。Redux则适用于大型复杂应用,具备严格的状态管理规则和丰富的社区支持,但配置较为繁琐。文章提供了两种方案的具体实现代码,并从适用场景、维护成本及社区支持三方面进行对比分析,帮助开发者根据项目需求选择最佳方案。
59 0
|
4月前
|
缓存 前端开发 JavaScript
前端常见的性能优化方案?
【7月更文挑战第14天】前端性能优化包括代码压缩、资源合并、图片优化、缓存策略和DOM操作改进。例如,压缩CSS、JS,懒加载图片,使用CDN,事件委托,启用HTTP/2,及利用性能工具进行评估和优化。目标是加快加载速度,提升用户体验和服务器效率。
75 2