详解JS的四种异步解决方案!

这篇具有很好参考价值的文章主要介绍了详解JS的四种异步解决方案!。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

同步&异步的概念

js中异步的应用场景

实现异步的四种方法

1、 回调函数

2、Promise

3、Generator

4、 async/await


        「异步编程」是前端工程师日常开发中经常会用到的技术,也是校招面试过程中常考的一个知识点。

        通过掌握「异步编程」的四种方式,可以让我们能够更好地处理JavaScript中的异步操作,提高代码的性能和用户体验。

        因此,「今天就想和大家来聊聊JS异步编程的四种方式!」

同步&异步的概念

在讲这四种异步方案之前,我们先来明确一下同步和异步的概念:

        所谓「同步(synchronization)」,简单来说,就是「顺序执行」,指的是同一时间只能做一件事情,只有目前正在执行的事情做完之后,才能做下一件事情。

        「同步操作的优点」在于做任何事情都是依次执行,井然有序,不会存在大家同时抢一个资源的问题。

        「同步操作的缺点」在于「会阻塞后续代码的执行」。如果当前执行的任务需要花费很长的时间,那么后面的程序就只能一直等待。

        所谓「异步(Asynchronization)」,指的是当前代码的执行不影响后面代码的执行。当程序运行到异步的代码时,会将该异步的代码作为任务放进「任务队列」,而不是推入主线程的调用栈。等主线程执行完之后,再去任务队列里执行对应的任务即可。

        因此,「异步操作的优点就是:不会阻塞后续代码的执行。」

js中异步的应用场景

开篇讲了同步和异步的概念,那么在JS中异步的应用场景有哪些呢?

  • 「定时任务」:setTimeout、setInterval

  • 「网络请求」:ajax请求、动态创建img标签的加载

  • 「事件监听器」:addEventListener

实现异步的四种方法

        对于「setTimeout、setInterval、addEventListener」这种异步场景,不需要我们手动实现异步,直接调用即可。

        但是对于「ajax请求」「node.js中操作数据库这种异步」,就需要我们自己来实现了~

1、 回调函数

在微任务队列出现之前,JS实现异步的主要方式就是通过「回调函数」

以一个简易版的Ajax请求为例,代码结构如下所示:

function ajax(obj){
 let default = {
   url: '...',
   type:'GET',
   async:true,
   contentType: 'application/json',
   success:function(){}
    };

 for (let key in obj) {
        defaultParam[key] = obj[key];
    }

    let xhr;
    if (window.XMLHttpRequest) {
        xhr = new XMLHttpRequest();
    } else {
        xhr = new ActiveXObject('Microsoft.XMLHTTP');
    }
    
    xhr.open(defaultParam.type, defaultParam.url+'?'+dataStr, defaultParam.async);
    xhr.send();
    xhr.onreadystatechange = function (){
        if (xhr.readyState === 4){
            if(xhr.status === 200){
                let result = JSON.parse(xhr.responseText);
                // 在此处调用回调函数
                defaultParam.success(result);
            }
        }
    }
}

我们在业务代码里可以这样调用「ajax请求」

ajax({
   url:'#',
   type:GET,
   success:function(e){
    // 回调函数里就是对请求结果的处理
   }
});

        「ajax请求」中的success方法就是一个回调函数,回调函数中执行的是我们请求成功之后要做的进一步操作。

        这样就初步实现了异步,但是回调函数有一个非常严重的缺点,那就是「回调地狱」的问题。

        大家可以试想一下,如果我们在回调函数里再发起一个ajax请求呢?那岂不是要在success函数里继续写一个ajax请求?那如果需要多级嵌套发起ajax请求呢?岂不是需要多级嵌套?

如果嵌套的层级很深的话,我们的代码结构可能就会变成这样:

js 异步,前端,javascript,okhttp,开发语言

        因此,为了解决回调地狱的问题,提出了「promise」「async/await」「generator」的概念。

2、Promise

「Promise」作为典型的微任务之一,它的出现可以使JS达到异步执行的效果。

一个「Promise函数」的结构如下列代码如下:

const promise = new Promise((resolve, reject) => {
 resolve('a');
});
promise
    .then((arg) => { console.log(`执行resolve,参数是${arg}`) })
    .catch((arg) => { console.log(`执行reject,参数是${arg}`) })
    .finally(() => { console.log('结束promise') });

        如果我们需要嵌套执行异步代码,相比于回调函数来说,「Promise」的执行方式如下列代码所示:

const promise = new Promise((resolve, reject) => {
 resolve(1);
});
promise.then((value) => {
     console.log(value);
     return value * 2;
    }).then((value) => {
     console.log(value);
     return value * 2;
    }).then((value) => {
    console.log(value);
    }).catch((err) => {
  console.log(err);
    });

即通过then来实现多级嵌套(「链式调用」),这看起来是不是就比回调函数舒服多了~

每个「Promise」都会经历的生命周期是:

  • 进行中(pending) :此时代码执行尚未结束,所以也叫未处理的(unsettled)

    已处理(settled) :异步代码已执行结束 已处理的代码会进入两种状态中的一种:
    • 已拒绝(rejected):遇到错误,异步代码执行失败 ,由reject()触发

    • 已完成(fulfilled):表明异步代码执行成功,由resolve()触发

因此,「pending」「fulfilled」「rejected」就是「Promise」中的三种状态啦~

        需要注意的是,在「Promise」中,要么包含resolve() 来表示 「Promise」 的状态为fulfilled,要么包含 reject() 来表示「Promise」的状态为rejected。

        不然我们的「Promise」就会一直处于pending的状态,直至程序崩溃...

除此之外,「Promise」不仅很好的解决了链式调用的问题,它还有很多高频的操作:

  • ·Promise.all(promises) :接收一个包含多个Promise对象的数组,等待所有都完成时,返回存放它们结果的数组。如果任一被拒绝,则立即抛出错误,其他已完成的结果会被忽略

  • ·Promise.allSettled(promises) : 接收一个包含多个Promise对象的数组,等待所有都已完成或者已拒绝时,返回存放它们结果对象的数组。每个结果对象的结构为{status:'fulfilled' // 或 'rejected', value // 或reason}

  • ·Promise.race(promises) : 接收一个包含多个Promise对象的数组,等待第一个有结果(完成/拒绝)的Promise,并把其result/error作为结果返回

示例代码如下所示:

function getPromises(){
    return [
        new Promise(((resolve, reject) => setTimeout(() => resolve(1), 1000))),
        new Promise(((resolve, reject) => setTimeout(() => reject(new Error('2')), 2000))),
        new Promise(((resolve, reject) => setTimeout(() => resolve(3), 3000))),
    ];
}

Promise.all(getPromises()).then(console.log);
Promise.allSettled(getPromises()).then(console.log);
Promise.race(getPromises()).then(console.log);

打印结果为:

js 异步,前端,javascript,okhttp,开发语言

js 异步,前端,javascript,okhttp,开发语言

js 异步,前端,javascript,okhttp,开发语言

3、Generator

        「generator」是ES6提出的一种异步编程的方案。因为手动创建一个iterator十分麻烦,因此ES6推出了「generator」,用于更方便的创建iterator。

        也就是说,「generator」就是一个返回值为iterator对象的函数。

在讲「generator」之前,我们先来看看iterator是什么:

iterator中文名叫「迭代器」。它为js中各种不同的数据结构(Object、Array、Set、Map)提供统一的访问机制。
任何数据结构只要部署iterator接口,就可以完成遍历操作。
因此iterator也是一种对象,不过相比于普通对象来说,它有着专为迭代而设计的接口。

我们通过一个例子来看看generator的特征:

function* createIterator() {
  yield 1;
  yield 2;
  yield 3;
}
// generators可以像正常函数一样被调用,不同的是会返回一个 iterator
let iterator = createIterator();
console.log(iterator.next().value); // 1
console.log(iterator.next().value); // 2
console.log(iterator.next().value); // 3

形式上,「generator」 函数是一个普通函数,但是有两个特征:

  • ·function关键字与函数名之间有一个星号

  • ·函数体内部使用yield语句,定义不同的内部状态

        在普通函数中,我们想要一个函数最终的执行结果,一般都是return出来,或者以return作为结束函数的标准。运行函数时也不能被打断,期间也不能从外部再传入值到函数体内。

        但在「generator」中,就打破了这几点,所以「generator」和普通的函数完全不同。

        当以function*  的方式声明了一个「generator」生成器时,内部是可以有许多状态的,以yield进行断点间隔。期间我们执行调用这个生成的「generator」,他会返回一个遍历器对象,用这个对象上的方法,实现获得一个yield后面输出的结果。

function* generator() {
    yield 1
    yield 2
};
let iterator = generator();
iterator.next()  // {value: 1, done: false}
iterator.next()  // {value: 2, done: false}
iterator.next()  // {value: undefined, done: true}

4、 async/await

最后我们来讲讲「async/await」,终于讲到这儿了!!!

「async/await」是ES7提出的关于异步的终极解决方案。我看网上关于「async/await」是谁的语法糖这块有两个版本:

  • 第一个版本说「async/await」是Generator的语法糖

  • 第二个版本说「async/await」是Promise的语法糖

其实,这两种说法都没有错。

「关于async/await是Generator的语法糖:」

        所谓generator语法糖,表明的就是「aysnc/await」实现的就是generator实现的功能。但是「async/await」比generator要好用。因为generator执行yield设下的断点采用的方式就是不断的调用iterator方法,这是个手动调用的过程。

        而async配合await得到的就是断点执行后的结果。因此「async/await」比generator使用更普遍。

「关于async/await是Promise的语法糖:」

如果不使用「async/await」的话,Promise就需要通过链式调用来依次执行then之后的代码:

function counter(n){
    return new Promise((resolve, reject) => { 
        resolve(n + 1);
    });
}

function adder(a, b){
    return new Promise((resolve, reject) => { 
        resolve(a + b);
    });
}

function delay(a){
    return new Promise((resolve, reject) => { 
        setTimeout(() => resolve(a), 1000);
    });
}
// 链式调用写法
function callAll(){
    counter(1)
       .then((val) => adder(val, 3))
       .then((val) => delay(val))
       .then(console.log);
}
callAll();//5

虽然相比于回调地狱来说,链式调用确实顺眼多了。但是其呈现仍然略繁琐了一些。

「async/await的出现,就使得我们可以通过同步代码来达到异步的效果」

async function callAll(){
   const count = await counter(1);
   const sum = await adder(count, 3);
   console.log(await delay(sum));
}
callAll();// 5

由此可见,「Promise搭配async/await的使用才是正解!」文章来源地址https://www.toymoban.com/news/detail-802460.html

到了这里,关于详解JS的四种异步解决方案!的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Selenium登录网页时,不定时出现异常弹窗的四种解决方案

    以下是一个简单的伪代码示例,展示了如何加入异常判断并重新登录: 在上述示例中,使用了 login() 函数进行登录操作,并根据返回值判断登录是否成功。然后,使用 check_usbkey_matching() 函数检查当前用户与USBKEY是否匹配,并根据返回值判断是否需要重新登录。 如果检测到当

    2024年04月25日
    浏览(27)
  • 解决VS中scanf()函数报错问题的四种方案(详细)

     scanf函数在VS中报错的主要原因是 scanf被认为不安全而被编译器默认设置为禁用。 那么如何解决这个问题呢 法一: 仅将函数 scanf 替换为 scanf_s 即可,其他语法不变。但scanf_s函数并不是C语言函数库里的标准函数,而是VS编译器所提供的函数,所以并不推荐用这种方法来解决

    2024年02月02日
    浏览(36)
  • Node:解决Error: error:0308010C:digital envelope routines::unsupported的四种解决方案

            主要是因为 nodeJs V17 版本发布了 OpenSSL3.0 对算法和秘钥大小增加了更为严格的限制,nodeJs v17 之前版本没影响,但 V17 和之后版本会出现这个错误。 我的node版本是v18+ 报错详细信息:    方案1:打开IDEA 终端,直接输入 Linux Mac OS: Windows: 方案2:打开IDEA 终端,直

    2024年04月13日
    浏览(32)
  • js跨域的解决方案

    指的是浏览器不能执行其他网站的脚本,简单来说是浏览器同源政策的限制,浏览器针对于ajax的限制。 同源政策 两个页面拥有相同的 协议,端口,域名 就是同源,如果有一个不相同就是不同源。 同源政策产生的目的 保护用户信息安全,防止一些网站盗取用户信息。 常见

    2024年02月10日
    浏览(27)
  • Three.js深度冲突(模型闪烁)与解决方案

    下面代码创建两个重合的矩形平面Mesh,通过浏览器预览,当你旋转三维场景的时候,你会发现模型渲染的时候产生闪烁。 这种现象,主要是两个Mesh重合,电脑GPU分不清谁在前谁在后,这种现象,可以称为深度冲突 Z-fighting 。 look 适当偏移,解决深度冲突,偏移尺寸相对模型

    2024年02月17日
    浏览(32)
  • 最简单的大屏适配解决方案---autofit.js

    在工作开发当中,我们避免不了要去做大屏。那么做大屏其实最难的点和最核心的问题就是适配, 下面为大家介绍最好用的大屏解决方案——autofit.js。 效果图展示,可根据窗口大小进行自动适配。 使用方法: 1.npm下载: 2.项目中引入使用: 3.init()初始化加载:注意一定要

    2024年02月08日
    浏览(35)
  • [javascript核心-09] 彻底解决js中的类型检测方案

    typeof 基于数据类型的值(二进制)进行检测 返回结果为字符串 typeof NaN 结果为 number typeof null 结果为 Object .对象存储以 000 开头,而 null 也是如此。 typeof 不能细分对象,结果都是 Object typeof function(){} 结果为 function instanceof 检测某个构造函数是否出现在某实例的原型链上 返回结

    2024年02月16日
    浏览(40)
  • js之删除对象属性的三种方法 & 判断对象中是否有某一属性的四种方法

    js之删除对象属性的三种方法 判断对象中是否有某一属性的四种方法 示例 1、基础版 2、进阶版 1、删除一个对象上的属性 1.1、delete 语法 delete 对象.属性名 1.2、es6之解构赋值 1.3、es6之反射 语法 Reflect.deleteProperty(对象,属性名) 2、判断对象中是否有某一属性的四种方法 2.1、

    2024年02月13日
    浏览(36)
  • Vue.js WebSocket 整合指南:实时通信的完美解决方案

    WebSocket是一种在Web应用程序中实现双向通信的通信协议,它允许客户端和服务器之间建立持久的、低延迟的连接,以实现实时数据传输。相比传统的HTTP请求,WebSocket更适合需要实时性和交互性的应用程序。 WebSocket解决了传统HTTP请求的一些限制,例如: 实时性: 传统HTTP请求需

    2024年02月04日
    浏览(36)
  • 安装了node.js,但是npm命令不可用的解决方案

    今天想创建一个vue项目,发现npm命令用不了了 第一步:检查是否安装了node.js 第二步:检查node.js的安装路径是否添加到了Path环境变量 右键单击【此电脑】,在显示的菜单里点击【属性】,此时会打开系统设置窗口,点击如下位置的【高级系统设置】,在弹出的系统属性小窗

    2024年02月11日
    浏览(54)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包