解决回调函数地狱问题

从回调地狱到链式Promise:解决JavaScript异步编程问题

回调函数地狱

  1. 再回调函数中嵌套回调函数,一直嵌套下去就形成了回调函数地狱
  2. 缺点:可读性差,异常无法捕获,耦合性严重,牵一发动全身

如下列代码:

<script>
	axios({ url: 'http://hmajax.itheima.net/api/province' }).then(result => {
	  const pname = result.data.list[0]
	  document.querySelector('.province').innerHTML = pname
	  // 获取第一个省份默认下属的第一个城市名字
	  axios({ url: 'http://hmajax.itheima.net/api/city', params: { pname } }).then(result => {
	    const cname = result.data.list[0]
	    document.querySelector('.city').innerHTML = cname
	    // 获取第一个城市默认下属第一个地区名字
	    axios({ url: 'http://hmajax.itheima.net/api/area', params: { pname, cname } }).then(result => {
	      document.querySelector('.area').innerHTML = result.data.list[0]
	    })
	  })
	})
</script>

Promise - 链式调用

  1. 概念:依靠 then() 方法会返回一个新生成的 Promise 对象特性,继续串联下一环任务,直到结束
  2. 细节:then() 回调函数中的返回值,会影响新生成的 Promise 对象最终状态和结果
  3. 好处:通过链式调用,解决回调函数嵌套问题
    请添加图片描述
  4. 按照图解,编写核心代码:
<script>
	/**
	 * 目标:掌握Promise的链式调用
	 * 需求:把省市的嵌套结构,改成链式调用的线性结构
	*/
	// 1. 创建Promise对象-模拟请求省份名字
	const p = new Promise((resolve, reject) => {
	  setTimeout(() => {
	    resolve('北京市')
	  }, 2000)
	})
	
	// 2. 获取省份名字
	const p2 = p.then(result => {
	  console.log(result)
	  // 3. 创建Promise对象-模拟请求城市名字
	  // return Promise对象最终状态和结果,影响到新的Promise对象
	  return new Promise((resolve, reject) => {
	    setTimeout(() => {
	      resolve(result + '--- 北京')
	    }, 2000)
	  })
	})
	
	// 4. 获取城市名字
	p2.then(result => {
	  console.log(result)
	})
	
	// then()原地的结果是一个新的Promise对象
	console.log(p2 === p) // false
</script>

## Promise-链式调用_解决回调地狱

  1. 做法:每个 Promise 对象中管理一个异步任务,用 then 返回 Promise 对象,串联起来
    请添加图片描述
  2. 按照图解思路,编写核心代码:
<script>
	/**
	 * 目标:把回调函数嵌套代码,改成Promise链式调用结构
	 * 需求:获取默认第一个省,第一个市,第一个地区并展示在下拉菜单中
	*/
	let pname = ''
	// 1. 得到-获取省份Promise对象
	axios({url: 'http://hmajax.itheima.net/api/province'}).then(result => {
	  pname = result.data.list[0]
	  document.querySelector('.province').innerHTML = pname
	  // 2. 得到-获取城市Promise对象
	  return axios({url: 'http://hmajax.itheima.net/api/city', params: { pname }})
	}).then(result => {
	  const cname = result.data.list[0]
	  document.querySelector('.city').innerHTML = cname
	  // 3. 得到-获取地区Promise对象
	  return axios({url: 'http://hmajax.itheima.net/api/area', params: { pname, cname }})
	}).then(result => {
	  console.log(result)
	  const areaName = result.data.list[0]
	  document.querySelector('.area').innerHTML = areaName
	})
</script>

async 函数和 await

  1. 概念:再 async 函数内,使用 await 关键字取代 then 函数,等待获取 Promise 对象成功状态的结果值
  2. 做法:使用 async 和 await 解决回调地狱问题
  3. 核心代码:
<script>
	/**
	 * 目标:掌握async和await语法,解决回调函数地狱
	 * 概念:在async函数内,使用await关键字,获取Promise对象"成功状态"结果值
	 * 注意:await必须用在async修饰的函数内(await会阻止"异步函数内"代码继续执行,原地等待结果)
	*/
	// 1. 定义async修饰函数
	async function getData() {
	  // 2. await等待Promise对象成功的结果
	  const pObj = await axios({url: 'http://hmajax.itheima.net/api/province'})
	  const pname = pObj.data.list[0]
	  const cObj = await axios({url: 'http://hmajax.itheima.net/api/city', params: { pname }})
	  const cname = cObj.data.list[0]
	  const aObj = await axios({url: 'http://hmajax.itheima.net/api/area', params: { pname, cname }})
	  const areaName = aObj.data.list[0]
	
	  document.querySelector('.province').innerHTML = pname
	  document.querySelector('.city').innerHTML = cname
	  document.querySelector('.area').innerHTML = areaName
	}
	
	getData()
</script>

async 函数和 await 捕获错误

  1. try 和 catch 的作用:语句标记要尝试的语句块,并指定一个出现异常时抛出的响应
<script>
	try {
	  // 要执行的代码
	} catch (error) {
	  // error 接收的是,错误消息
	  // try 里代码,如果有错误,直接进入这里执行
	}
</script>

try 里有报错的代码,会立即跳转到 catch 中
2. 尝试把代码中 url 地址写错,运行观察 try catch 的捕获错误信息能力

<script>
	/**
	 * 目标:async和await_错误捕获
	*/
	async function getData() {
	  // 1. try包裹可能产生错误的代码
	  try {
	    const pObj = await axios({ url: 'http://hmajax.itheima.net/api/province' })
	    const pname = pObj.data.list[0]
	    const cObj = await axios({ url: 'http://hmajax.itheima.net/api/city', params: { pname } })
	    const cname = cObj.data.list[0]
	    const aObj = await axios({ url: 'http://hmajax.itheima.net/api/area', params: { pname, cname } })
	    const areaName = aObj.data.list[0]
	
	    document.querySelector('.province').innerHTML = pname
	    document.querySelector('.city').innerHTML = cname
	    document.querySelector('.area').innerHTML = areaName
	  } catch (error) {
	    // 2. 接着调用catch块,接收错误信息
	    // 如果try里某行代码报错后,try中剩余的代码不会执行了
	    console.dir(error)
	  }
	}
	
	getData()
</script>
### 使用Promise解决回调地狱问题 在传统的JavaScript中,异步操作通常通过回调函数来实现。然而,当多个异步操作需要按顺序执行时,嵌套的回调函数会导致代码难以维护和阅读,这种情况被称为“回调地狱”。为了解决这一问题,可以使用Promise对象来替代回调函数。 #### 什么是Promise? Promise是一个表示异步操作最终完成或失败的对象。它有三种状态:待定(pending)、已实现(fulfilled)和已拒绝(rejected)。通过链式调用 `.then()` 和 `.catch()` 方法,可以避免多层嵌套的回调函数[^2]。 #### 示例代码 以下是一个使用Promise替代回调函数的示例: ```javascript function asyncOperation1() { return new Promise((resolve, reject) => { setTimeout(() => { resolve("操作1完成"); }, 1000); }); } function asyncOperation2(data) { return new Promise((resolve, reject) => { setTimeout(() => { resolve(data + ",操作2完成"); }, 1000); }); } function asyncOperation3(data) { return new Promise((resolve, reject) => { setTimeout(() => { resolve(data + ",操作3完成"); }, 1000); }); } asyncOperation1() .then((result1) => { console.log(result1); // 输出: 操作1完成 return asyncOperation2(result1); }) .then((result2) => { console.log(result2); // 输出: 操作1完成,操作2完成 return asyncOperation3(result2); }) .then((result3) => { console.log(result3); // 输出: 操作1完成,操作2完成,操作3完成 }) .catch((error) => { console.error("发生错误:", error); }); ``` 在这个例子中,每个异步操作都返回一个Promise对象。通过 `.then()` 方法,可以在前一个异步操作完成后执行下一个操作,从而避免了回调地狱问题[^5]。 #### 错误处理 Promise还提供了一个 `.catch()` 方法,用于捕获整个链中的任何错误。如果任何一个Promise被拒绝(reject),`.catch()` 方法将被触发,并接收拒绝的原因作为参数[^4]。 #### 使用`finally`方法 无论Promise是否成功或失败,`finally`方法都会被执行。这在清理资源或显示加载指示器等场景中非常有用[^4]。 ```javascript asyncOperation1() .then((result1) => { console.log(result1); return asyncOperation2(result1); }) .then((result2) => { console.log(result2); return asyncOperation3(result2); }) .catch((error) => { console.error("发生错误:", error); }) .finally(() => { console.log("所有操作完成"); }); ``` #### 总结 通过使用Promise,可以将嵌套的回调函数转换为链式调用,从而使代码更加清晰、易于维护。同时,Promise提供了强大的错误处理机制,能够更好地管理异步操作中的异常情况[^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值