谷粒商城实战笔记-34-前端基础-ES6-promise异步编排

文章目录

在ES6中,Promise是一个用于异步编程的重要构造,它代表了一个最终可能完成或失败的异步操作,并且具有一个最终的值。

简单的说,Promise提供了一种新的异步调用的语法,让异步调用的语法更加简洁和容易理解。

接下里,我们将以一个例子深入浅出的说清楚Promise的来龙去脉。

一,回调地狱(Callback Hell)

回调地狱也被称为金字塔之痛(Pyramid of Doom),是在使用回调函数进行异步编程时遇到的一个常见问题。

在JavaScript中,回调函数经常被用来处理异步操作的结果,如网络请求、定时器、文件I/O等。

当多个异步操作需要按顺序执行,并且每个操作依赖于前一个操作的结果时,代码中可能会出现多层嵌套的回调函数,这会导致代码结构变得非常复杂和难以维护。

例如,考虑以下伪代码:

javascript 复制代码
someAsyncOperation(function(result1) {
  anotherAsyncOperation(result1, function(result2) {
    yetAnotherAsyncOperation(result2, function(result3) {
      // ...更多的嵌套...
    });
  });
});

在这个例子中,每个异步操作都必须在其完成后调用一个回调函数,而这个回调函数可能又会触发另一个异步操作。这种深度嵌套的结构会导致代码变得难以阅读和调试,尤其是当异步操作的数量增加时。

回调地狱的几个主要问题包括:

  • 可读性差:代码的逻辑流程不易追踪,因为控制流被分割成许多嵌套的块。
  • 难以维护:修改代码或添加新的异步操作可能需要深入到多层嵌套中,这增加了引入错误的风险。
  • 错误处理复杂:在多层嵌套中处理错误通常需要在每个回调中重复错误处理代码,这可能导致代码冗余和潜在的遗漏。

为了解决回调地狱问题,现代JavaScript引入了更好的异步编程模型,如Promises和async/await语法。这些新特性提供了一种更简洁、更线性的方式来处理异步操作,减少了代码的复杂度,同时提高了可读性和可维护性。例如,使用Promises,上面的例子可以重写为:

javascript 复制代码
someAsyncOperation()
  .then(result1 => anotherAsyncOperation(result1))
  .then(result2 => yetAnotherAsyncOperation(result2))
  .then(result3 => {
    // ...处理result3...
  })
  .catch(error => {
    // ...统一处理错误...
  });

或者使用async/await:

javascript 复制代码
async function doOperations() {
  try {
    const result1 = await someAsyncOperation();
    const result2 = await anotherAsyncOperation(result1);
    const result3 = await yetAnotherAsyncOperation(result2);
    // ...处理result3...
  } catch (error) {
    // ...统一处理错误...
  }
}

这些替代方案极大地改善了代码的结构和可读性,使得异步编程变得更加优雅和直观。

二,实战Promise

1,场景说明

假设有这样一个需求,学生登录学校的考试系统,查询某一个的成绩。

实现这个简单的需求,要发出三次请求。

  • 第一次请求,根据用户名查询用户ID,查询成功说明可以登录
  • 第一次请求成功后,发出第二次请求,查询该用户有成绩的科目ID
  • 第二次请求成功后,发出第三次请求,根据科目ID查询成绩

假设后台的数据存储在json文件中,整个请求过程如下。

第一次请求从后台的user.json文件中查询到用户的ID为1。

clike 复制代码
// user.json:
{
	"id": 1,
	"name": "zhangsan",
	"password": "123456"
}

第二次请求从后台的user_corse_1.json中查询到用户有成绩的课程ID为10。

clike 复制代码
// user_corse_1.json:
{
	"id": 10,
	"name": "chinese"
}

第三次请求从后台的corse_score_10.json中查询到用户课程ID成绩为90。

clike 复制代码
corse_score_10.json:
{
	"id": 100,
	"score": 90
}

2,回调地狱-传统实现

假设我们用JQuery的ajax实现异步请求,代码如下。

html代码完整代码如下,后续的JS演示代码都放在<script>部分。

clike 复制代码
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>

</head>
<body>
    <script>

    </script>
</body>
</html>
clike 复制代码
$.ajax({
    // 第一次请求
	url: "mock/user.json",
	success(data) {
		console.log("查询用户:", data);
		$.ajax({
		    // 第二次请求
			url: `mock/user_corse_${data.id}.json`,
			success(data) {
				console.log("查询到课程:", data);
				$.ajax({
				    // 第三次请求
					url: `mock/corse_score_${data.id}.json`,
					success(data) {
						console.log("查询到分数:", data);
					},
					error(error) {
						console.log("出现异常了:" + error);
					}
				});
			},
			error(error) {
				console.log("出现异常了:" + error);
			}
		});
	},
	error(error) {
		console.log("出现异常了:" + error);
	}
});

上面代码仅仅有三次回调,如果回答次数更多,嵌套的更深,导致代码难以阅读和理解,这就是所谓的回调地狱

3,使用Promise重构

3.1 用Promise实现上述需求

clike 复制代码
new Promise((resolve, reject)=>{

            $.ajax(
                {
                    url: "mock/user.json",
                    success(data) {
                        console.log("Promise查询用户:", data);
                        resolve(data.id);
                    },
                    error(error) {
                        console.log("Promise出现异常了:" + error);
                        reject(error);
                    }
                }
            );
        }).then(id=>{
            return new Promise((resolve, reject)=>{
                $.ajax(
                    {
                        url: `mock/user_corse_${id}.json`,
                        success(data) {
                            console.log("Promise查询到课程:", data);
                            resolve(data.id);
                        },
                        error(error) {
                            console.log("Promise出现异常了:" + error);
                            reject(error);
                        }
                    }
                );
            });
        }).then(id=>{
            return new Promise((resolve, reject)=>{
                $.ajax(
                    {
                        url: `mock/corse_score_${id}.json`,
                        success(data) {
                            console.log("Promise查询到分数:", data);
                            resolve(data);
                        },
                        error(error) {
                            console.log("Promise出现异常了:" + error);
                            reject(error);
                        }
                    }
                );
            });
        }).then(({score})=>{
            console.log("Promise查询到分数:", score);
        })

3.2 进一步重构

下面通过抽取函数、复用代码,对上述代码进行进一步重构,使之更为简洁。

把发送请求的代码抽象为一个公共的方法,减少冗余代码,代码结构更为清晰易懂。

clike 复制代码
let sendRequest = function (url, params) { 
	return new Promise((resolve, reject) => {
		$.ajax({
			url: url,
			type: "GET",
			data: params,
			success(result) {
				resolve(result);
			},
			error(error) {
				reject(error);
			}
		});
	})
}

于是代码可以重构为:

clike 复制代码
var sendRequest = function (url, params) {
            return new Promise((resolve, reject) => {
                $.ajax({
                    url: url,
                    type: "GET",
                    data: params,
                    success(result) {
                        console.log(result);
                        resolve(result.id);
                    },
                    error(error) {
                        reject(error);
                    }
                });
            })
        }

        sendRequest("mock/user.json")
        .then(id => {
            console.log("Promise111查询到id:", id);
            return sendRequest(`mock/user_corse_${id}.json`);
        }).then(id => {
            console.log("Promise1113301查询到id: " + id);
            return sendRequest(`mock/corse_score_${id}.json`);
        }).then(({ score }) => {
            console.log("Promise222查询到分数:", score);
        })

经过重构后的代码明显变得简洁易读的多,代码的编写更加符合人脑习惯的流式思考方式。

当然,Promise也是对异步回调的封装,使用起来确实要简单多了,但也增加了理解难度,需要对Promise有一定的熟悉度,否则也是很难理解的,这就要求我们多写多思考。

相关推荐
大筒木老辈子3 分钟前
Linux笔记---进程:初识进程
linux·服务器·笔记
2401_8791036825 分钟前
24.11.23 Ajax
笔记·ajax
上官花雨27 分钟前
什么是axios?怎么使用axios封装Ajax?
前端·ajax·okhttp
米奇妙妙wuu29 分钟前
React中 setState 是同步的还是异步的?调和阶段 setState 干了什么?
前端·javascript·react.js
李刚大人31 分钟前
react-amap海量点优化
前端·react.js·前端框架
闹闹没有闹1 小时前
socket连接封装
前端
qq_364371722 小时前
Vue 内置组件 keep-alive 中 LRU 缓存淘汰策略和实现
前端·vue.js·缓存
y先森3 小时前
CSS3中的弹性布局之侧轴的对齐方式
前端·css·css3
xiaoyalian7 小时前
R语言绘图过程中遇到图例的图块中出现字符“a“的解决方法
笔记·r语言·数据可视化
y先森8 小时前
CSS3中的伸缩盒模型(弹性盒子、弹性布局)之伸缩容器、伸缩项目、主轴方向、主轴换行方式、复合属性flex-flow
前端·css·css3