跳转至

异步编程#

约 411 个字 5 行代码 预计阅读时间 8 分钟

使用回调的异步编程#

延时任务#

setTimeout用于在指定的时间后执行一次函数。它接受两个参数:要执行的函数和延迟的时间(以毫秒为单位)。

HTML
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>延时任务</title>
</head>
<body>
<div id="root"></div>
<!--封装print方法,用于页面信息输出-->
<script src="../js/print.js"></script>
<script>
    print("准备执行异步方法")
    /*
    * 会在1秒后调用回调函数
    * */
    setTimeout(callBack, 1000)
    print("主程序会继续执行,并不会等待延时任务执行完再执行")

    /*
    * 回调函数
    * */
    function callBack() {
        print("1秒后,回调执行")
    }
</script>
</body>
</html>

定时任务#

setInterval用于在指定的时间间隔内重复执行一次函数。它接受两个参数:要执行的函数和间隔时间(以毫秒为单位)。

可以使用clearInterval来停止定时任务。

HTML
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>定时任务</title>
</head>
<body>
<div id="root"></div>
<!--封装print方法,用于页面信息输出-->
<script src="../js/print.js"></script>
<script>
    print("准备执行异步方法")
    /*
    * 每隔一秒执行一次回调
    * */
    const interval = setInterval(callBack, 1000);
    print("主程序会继续执行,会不会等待定时任务执行完再执行")

    let i = 0;

    function callBack() {
        print("1秒后,回调执行")
        if (++i === 5) {
            clearInterval(interval)
        }
    }
</script>
</body>
</html>

事件#

通过addEventListener来注册回调函数,当指定事件发时会调用这些函数。

HTML
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>事件</title>
</head>
<body>
<button id="btn">点击</button>

<script>
    const btn = document.getElementById('btn');
    // 通过addEventListener注册回调函数
    btn.addEventListener('click', buttonClick);

    // 回调函数
    function buttonClick() {
        alert("按钮被点击了")
    }
</script>
</body>
</html>

网络请求#

HTML
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>网络请求</title>
</head>
<body>
<div id="root"></div>
<!--封装print方法,用于页面信息输出-->
<script src="../js/print.js"></script>

<script>
    print("准备发送网络请求")

    const request = new XMLHttpRequest();
    request.open("GET", "https://httpbin.org/");
    request.send();
    // 注册响应到达的回调函数
    request.onload = function () {
        print("收到响应,响应码为:" + request.status)
    }
    // 注册网络出错的回调
    request.onerror = function (e) {
        print("网络错误:" + e.type)
    }

    print("主程序会继续执行,不会等待网络请求收到响应后再执行。")
</script>
</body>
</html>

期约(Promise)#

回调地狱问题#

  • 基于回调的异步编程会出现回调多层嵌套的情况,导致代码缩进过多难以阅读。
  • 回调函数中发生异常,异步的发起者无法获取和处理异常。
HTML
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>回调地狱</title>
</head>
<body>
<div id="root"></div>
<!--封装print方法,用于页面信息输出-->
<script src="../js/print.js"></script>

<script>
    setTimeout(() => {
        print("执行第一次回调");
        setTimeout(() => {
            print("第一次回调结束,执行第二次回调")
            setTimeout(() => {
                print("第二次回调结束,执行第三次回调")
                setTimeout(() => {
                    print("第三次回调结束,执行第四次回调")
                    setTimeout(() => {
                        print("第四次回调结束,执行第五次回调")
                        throw new Error("发生异常")
                    }, 1000)
                }, 1000)
            }, 1000)
        }, 1000)
    }, 1000)
</script>
</body>
</html>

期约概述#

期约(Promise)是为了简化异步编程。

期约是一个对象,表示异步操作的结果。

相比于回调处理异步操作,期约做出如下改进:

  • 期约可以让回调地狱以一种更线性的期约链形式表达出来。
  • 期约标准化了异常处理,通过期约链提供了一种让错误正确传播的途径。

Warning

不能使用期约替代setInterval。期约不能表示重复的异步计算,这并不是期约所考虑的用例。

也不应该使用期约替代事件,事件往往可以重复触发,不需要等待上一条事件执行完毕。

示例#

期约链#

期约链更容易表达一连串异步操作。

评论