【H2O2|全栈】JS进阶知识(十)ES6(6)

目录

前言

开篇语

准备工作

同步和异步

概念

优缺点

案例

同步情况

异步情况

回调地域

Promise

概念

状态

成功状态

实例函数

案例

封装AJAX

优化回调地域

拓展------await

总结

异常

try-catch-finally

error

throw

结束语


前言

开篇语

本系列博客主要分享JavaScript的进阶语法知识,本期为第十期,依然围绕ES6的语法进行展开。

本期内容为:同步和异步、回调地域、Promise和异常。

与基础部分的语法相比,ES6的语法进行了一些更加严谨的约束和优化,因此,在之后使用原生JS时,我们应该尽量使用ES6的语法进行代码编写。

准备工作

软件:【参考版本】Visual Studio Code

**插件(扩展包):**Open in browser, Live Preview, Live Server, Tencent Cloud AI Code Assistant, htmltagwrap

提示:在不熟练的阶段建议关闭AI助手

**浏览器版本:**Chrome

系统版本: Win10/11/其他非Windows版本

同步和异步

概念

同步操作是指,程序由上至下顺序执行,后面的数据需要上一次执行的结果。

异步操作是指,多个功能并发(同时)执行,我们此前学过的计时器、AJAX都是支持异步操作的。

优缺点

同步操作的优点是数据安全,按顺序接收数据和执行。缺点是顺序执行的效率比较低

异步操作的优点是执行速度快。缺点是存在安全性问题,明明需要先执行的步骤,却因为异步条件不符合而被推迟到之后了。

案例

现在有这么一个案例,将大象放入冰箱,总共需要几步?

其实,也就下面三个步骤------

  1. 打开冰箱门

  2. 大象放进去

  3. 关闭冰箱门

我们为这三个步骤封装三个方法------

javascript 复制代码
function open() {
    console.log('打开冰箱门');
}

function putIn() {
    console.log('大象放进去');
}

function close() {
    console.log('关闭冰箱门');
}

同步情况

首先,我们来看看在同步情况下三个方法的执行情况。

我们封装一个执行函数exec(),它的参数为回调函数,由回调函数来决定执行哪一个功能,就像下面这样------

javascript 复制代码
function exec(callback) {
    callback();
}

那么,我们想要顺序执行上面的三个事件,就可以这么做------

javascript 复制代码
exec(open);
exec(putIn);
exec(close);

异步情况

现在,我们为上面的三个步骤添加延时时间,即添加一个计时器。

我们假设第一个步骤需要延时3000ms完成,第二个步骤需要延时2000ms,最后一步需要延时1000ms,那么,我们的执行函数就需要更改成下面的形式------

javascript 复制代码
function exec(callback, time) {
    setTimeOut = (callback, time);
}

注意,这里的callback实际上就是计时器中的() => callback(),由于箭头函数本质上就是用于执行回调函数,所以这里直接写回调函数名即可(不要写括号,箭头函数没有在参数中调用)。

这是,如果我们再次像之前那样直接写三个exec()函数,由于三者异步执行,于是出现了下面的结果------

javascript 复制代码
exec(open, 3000);
exec(putIn, 2000);
exec(close, 1000);

可以看到,明明需要先执行的打开冰箱门的步骤,却因为在并发操作中延时最久,在最后一步才执行,导致出现了上面的荒唐的结果。

所以,对于这类情况,我们需要将原本的并发执行的异步情况转化为顺序执行的同步情况,确保所有的步骤都能安全地执行,这也是本节的重点。

回调地域

为了解决异步操作之间的顺序问题,我们可以在exec()上做出一些小小的处理。

我们先执行完第一步,再执行第二步,之后的操作相同。

在callback的位置,我们知道callback和() => callback()是等价的,所以我们将该形参展开为箭头函数,而第二形参time保持不变------

javascript 复制代码
exec(() => {
    open();
},3000);

在open()执行完毕后,我们再次执行exec(),这一次执行putIn(),就像下面这样------

javascript 复制代码
exec(() => {
    open();
    exec(() => {
        putIn();
    },2000);
},3000);

由此一来,我们就成功实现了先执行完第一步,再执行第二步的操作,第三步也是一样的。

javascript 复制代码
exec(() => {
    open();
    exec(() => {
        putIn();
        exec(() => {
            close();
        }, 1000);
    }, 2000);
}, 3000);

这样一种将异步操作转为同步操作的方式,就是回调地域

但是,不难发现,对于这种转化的方式,整个程序在排列上是按照纵向排列的。

当我们执行的步骤很多时,就需要不断地进行这种回调的过程,最后导致整个程序的代码量将会非常庞大,不方便我们进行代码的调试工作

Promise

概念

Promise就是一个构造函数,构造函数中有一个参数,该参数是一个函数。这个函数有两个参数resolved,rejected,分别代表两个回调函数。

于是,Promise就可以写成下面这样------

javascript 复制代码
Promise((resolved, rejected) => {
    // 调用的回调函数
})

状态

一般来说,Promise有三种状态------准备(pending)、成功(fulfilled/resolved)和失败(rejected)。

首先,我们为Promise创建一个实例化对象,以便查看这些状态------

javascript 复制代码
const p = new Promise(() => { });

如果不调用任何回调函数,表示准备状态,控制台打印结果如下------

如果调用**resolved()**函数,表示成功状态,控制台打印的结果如下------

javascript 复制代码
const p = new Promise((resolved, rejected) => {
    resolved();
});

如果调用**rejected()**函数,表示失败状态,控制台打印的结果如下------

javascript 复制代码
const p = new Promise((resolved, rejected) => {
    rejected();
});

成功状态

这也是一道经典的面试题,一般来说,成功的状态有下面这些------

  1. 初始化 resolved函数

  2. 没有报错 但是没有返回值

  3. 没有报错 但是有返回值 返回值作为下一个then的接收参数

  4. Promise有一个静态函数 resolve

其中resolved()是直接调用的,静态函数是由**Promise.resolve()**调用的。

实例函数

Promise有三种常用的实例函数------then(),catch(),finally()。

**then()**有两个参数,分别是两个回调函数,第一个函数在接受成功的情况下执行,第二个函数在接受失败的情况下执行。只写一个参数,代表成功的情况。

**catch()**仅有一个参数,仅用于接收失败的情况,一般来说失败的情况都由该方法接受。

**finally()**没有参数,无论成功还是失败都需要接受参数。

案例

封装AJAX

假设我们现在有三个文本文件,请用ajax来按顺序接受他们的参数。

由于ajax为异步操作,所以我们可以使用Promise将这三次ajax转为同步操作。

在ajax返回success()时,调用resolved();返回error()时,调用rejected()------

javascript 复制代码
function getAjax(url) {
    return new Promise((resolved, rejected) => {
        $.ajax({
            url,
            success(res) {
                // ajax成功时调用resolved
                resolved(res);
            },
            error(res) {
                // ajax失败时调用rejected
                rejected(res);
            }
        })
    })
}

为简单起见,这里暂时只考虑成功的情况,通过resolved()的参数,可以获取success()接收的数据。

而then()可以拿到resolved()的参数或者上一次return的结果,于是,我们可以在then()中接收上一步ajax()获取的数据,并返回下一次getAjax()的结果。

javascript 复制代码
getAjax(url1)
    .then(data => {
        // 将数据从ajax中通过resolved的参数拿出来
        console.log(data);
        // 进行下一次的ajax操作,返回的参数交给下一次的then
        return getAjax(url2);
    }).then(data => {
        console.log(data);
        return getAjax(url3);
    }).then(data => console.log(data));

优化回调地域

对于之前的大象放进冰箱的案例,我们也可以利用Promise来优化。

思路是,让exec()方法返回一个Promise()对象,然后再将这个对象交给后续的then()接收,由此往复。

javascript 复制代码
exec(open, 3000).then(() => exec(in1, 2000)).then(() => exec(close, 1000))

拓展------await

其实,上述使用then的过程,还可以使用另一种等价的方式来实现------

javascript 复制代码
await exec(open, 3000);
await exec(putIn, 2000);
await exec(close, 1000);

await可以让当前的方法执行完毕之后,再执行后续的方法。

但await必须在使用async修饰的函数中使用,它用于将同步函数转为异步函数

javascript 复制代码
async function fn() {
    await exec(open, 3000);
    await exec(putIn, 2000);
    await exec(close, 1000);
}

fn();

当同步函数返回了一个Promise对象后,由于then()的函数式异步,从而将异步转为同步

实质上,await()返回的数据就是Promise的then()返回的数据。

总结

常见的异步操作汇总如下------

计时器: setTimeout【经过多少毫秒执行一次】/ setInterval【每隔多少毫秒执行一次】;

ajax:jquery的ajax方法的async参数为true时(默认状态)为异步;

③Promise的**then() / catch()**是异步。

事件是异步的。

异常

try-catch-finally

当我们认为一段代码可能会出现错误时,就可以尝试(try)执行这段代码,并捕获(catch)其中可能出现的错误,为了不让这个错误阻断我们的程序,我们还需要让程序抛出错误之后还能最终执行(finally)

error

如果我们捕获到了这个错误,并想要让这个错误在控制台输出出来,就可以catch(err),并使用**console.error(err)**将这个错误输出。形参err就是我们捕获的错误。

throw

对于异常而言,有一些常见的异常对象,由new + 构造函数获取,并由throw抛出。

由于常见的异常对象的父类都是Error()类型的,所以我们也常常使用下面的形式统一抛出错误------

javascript 复制代码
throw new Error('自定义的错误提示语句');

比如,我们定义一个常量a,当常量a的值小于100时,我们抛出自定义的异常------

javascript 复制代码
const a = 10;
if(a < 100) {
    throw new Error("错误:a不足100");
}

结束语

本期内容到此结束。关于本系列的其他博客,可以查看我的JS进阶专栏。

在全栈领域,博主也只不过是一个普通的萌新而已。本系列的博客主要是记录一下自己学习的一些经历,然后把自己领悟到的一些东西总结一下,分享给大家。

文章全篇的操作过程都是笔者亲自操作完成的,一些定义性的文字加入了笔者自己的很多理解在里面,所以仅供参考。如果有说的不对的地方,还请谅解。

==期待与你在下一期博客中再次相遇==

------临期的【H2O2】

相关推荐
蒜蓉大猩猩7 分钟前
Vue.js - 组件化编程
开发语言·前端·javascript·vue.js·前端框架·ecmascript
南鸳61019 分钟前
Scala:根据身份证号码,输出这个人的籍贯
开发语言·后端·scala
Clockwiseee26 分钟前
JS原型、原型链以及原型链污染学习
javascript·学习·原型模式
eclipsercp31 分钟前
PyQt5:Python GUI开发的超级英雄
开发语言·python·qt
王解37 分钟前
一篇文章读懂 Prettier CLI 命令:从基础到进阶 (3)
前端·perttier
军训猫猫头38 分钟前
44.扫雷第二部分、放置随机的雷,扫雷,炸死或成功 C语言
c语言·开发语言
北巷!!39 分钟前
宇信科技JAVA笔试(2024-11-26日 全部AK)
java·开发语言·科技
ᝰꫝꪉꪯꫀ36140 分钟前
JavaWeb——SpringBoot原理
java·开发语言·后端·springboot
乐闻x43 分钟前
最佳实践:如何在 Vue.js 项目中使用 Jest 进行单元测试
前端·vue.js·单元测试
Octopus207743 分钟前
【C++】读取数量不定的输入数据
开发语言·c++·笔记·学习