es6知识点4-自备使用

先说明一下:Promiseclass 属于 ES6,async/await 严格来说是后续 ES2017 加入的,但现代 JavaScript 实战里通常会一起学。

10. Promise:处理异步任务

先理解为什么需要 Promise。

JavaScript 经常遇到这种事情:

js 复制代码
调用接口
↓
等待后端返回
↓
拿到结果
↓
继续处理

但是接口请求不会立即完成。

比如:

js 复制代码
const response = axios.get('/user/1')

这里 response 并不是后端真正返回的数据,因为请求还没完成。

所以 JavaScript 需要一种机制:

等异步操作完成以后,再执行后面的代码。

Promise 就是为这个设计的。


10.1 Promise 的三个状态

一个 Promise 有三个状态:

text 复制代码
pending
进行中

fulfilled
成功

rejected
失败

整个过程只能:

text 复制代码
pending
   ↓
fulfilled

或者:

text 复制代码
pending
   ↓
rejected

一旦成功或者失败,就不会再改变。


10.2 最基础写法

js 复制代码
const promise = new Promise((resolve, reject) => {
  const success = true

  if (success) {
    resolve('操作成功')
  } else {
    reject('操作失败')
  }
})

这里:

js 复制代码
resolve(...)

表示:

成功。

而:

js 复制代码
reject(...)

表示:

失败。


10.3 怎么接收 Promise 的结果

使用:

js 复制代码
.then()

和:

js 复制代码
.catch()

例如:

js 复制代码
promise
  .then(result => {
    console.log(result)
  })
  .catch(error => {
    console.log(error)
  })

如果执行:

js 复制代码
resolve('操作成功')

就进入:

js 复制代码
.then()

如果:

js 复制代码
reject('操作失败')

就进入:

js 复制代码
.catch()

所以可以先记:

text 复制代码
resolve
↓
then

reject
↓
catch

10.4 Axios 为什么可以 .then()

比如你平时写:

js 复制代码
getUser(1).then(response => {
  console.log(response.data)
})

之所以可以调用:

js 复制代码
.then()

就是因为:

js 复制代码
getUser(1)

返回的是一个 Promise。

例如若依里面经常:

js 复制代码
export function getUser(userId) {
  return request({
    url: `/system/user/${userId}`,
    method: 'get'
  })
}

这里:

js 复制代码
request(...)

本质上返回 Promise。

所以:

js 复制代码
getUser(1)

也就返回 Promise。

因此你才能:

js 复制代码
getUser(1).then(...)

10.5 Promise 链式调用

Promise 很重要的能力是:

js 复制代码
.then()
.then()
.then()

例如:

js 复制代码
getUser(1)
  .then(response => {
    return response.data
  })
  .then(user => {
    return user.name
  })
  .then(name => {
    console.log(name)
  })

假设接口返回:

js 复制代码
{
  data: {
    id: 1,
    name: '张三'
  }
}

执行过程:

text 复制代码
response
↓
response.data
↓
user
↓
user.name
↓
张三

为什么可以继续 .then()

因为 .then() 会返回一个新的 Promise。


10.6 return 特别重要

看看:

js 复制代码
getUser(1)
  .then(response => {
    return response.data
  })
  .then(data => {
    console.log(data)
  })

这里:

js 复制代码
return response.data

会把数据传给下一个:

js 复制代码
.then(data => {})

如果你忘记:

js 复制代码
getUser(1)
  .then(response => {
    response.data
  })
  .then(data => {
    console.log(data)
  })

那么:

js 复制代码
data

就是:

js 复制代码
undefined

所以 Promise 链里:

想把数据交给下一个 .then(),通常就要 return


10.7 返回另一个 Promise

这个更重要。

例如:

js 复制代码
getUser(1)
  .then(response => {
    const userId = response.data.id

    return getOrders(userId)
  })
  .then(response => {
    console.log(response.data)
  })

执行顺序:

text 复制代码
先查询用户
↓
拿到 userId
↓
查询用户订单
↓
拿到订单

这里:

js 复制代码
return getOrders(userId)

返回的是另一个 Promise。

JavaScript 会等这个 Promise 执行完成后,再进入下一个 .then()

这就是 Promise 链。


10.8 catch

例如:

js 复制代码
getUser(1)
  .then(response => {
    return response.data
  })
  .catch(error => {
    console.error(error)
  })

如果请求失败,或者 .then() 里面代码报错:

js 复制代码
throw new Error('处理失败')

一般都会进入:

js 复制代码
.catch()

10.9 finally

还有:

js 复制代码
.finally()

不管成功还是失败都会执行。

例如 Loading:

js 复制代码
this.loading = true

getUser(1)
  .then(response => {
    this.user = response.data
  })
  .catch(error => {
    console.error(error)
  })
  .finally(() => {
    this.loading = false
  })

特别适合:

text 复制代码
关闭 loading
释放资源
恢复按钮状态

11. async / await

async / await 是 Promise 的更现代写法。

重点:

它并不是另一套异步机制。

本质上还是 Promise。

只是让异步代码看起来更像同步代码。


11.1 Promise 写法

例如:

js 复制代码
getUser(1).then(response => {
  console.log(response.data)
})

使用 async/await

js 复制代码
const response = await getUser(1)

console.log(response.data)

11.2 await 是什么意思

js 复制代码
const response = await getUser(1)

可以理解:

text 复制代码
执行 getUser(1)
↓
它返回 Promise
↓
等待 Promise 成功
↓
把成功结果赋值给 response

所以:

js 复制代码
const response = await getUser(1)

大致等价于:

js 复制代码
getUser(1).then(response => {
})

11.3 await 一般必须放在 async 函数里

错误:

js 复制代码
function getData() {
  const response = await getUser(1)
}

正确:

js 复制代码
async function getData() {
  const response = await getUser(1)
}

或者:

js 复制代码
const getData = async () => {
  const response = await getUser(1)
}

11.4 Vue 实战

以前:

js 复制代码
getList() {
  listOrder(this.queryParams).then(response => {
    this.orderList = response.rows
    this.total = response.total
  })
}

可以改成:

js 复制代码
async getList() {
  const response = await listOrder(this.queryParams)

  this.orderList = response.rows
  this.total = response.total
}

我个人更推荐第二种。

因为当业务逻辑复杂以后,可读性明显更好。


11.5 连续多个接口时区别特别明显

假设:

text 复制代码
查询用户
↓
查询订单
↓
查询订单详情

Promise:

js 复制代码
getUser(1)
  .then(response => {
    const userId = response.data.id
    return getOrders(userId)
  })
  .then(response => {
    const orderId = response.data[0].id
    return getOrderDetail(orderId)
  })
  .then(response => {
    console.log(response.data)
  })

使用 async/await

js 复制代码
async function loadData() {
  const userResponse = await getUser(1)
  const userId = userResponse.data.id

  const orderResponse = await getOrders(userId)
  const orderId = orderResponse.data[0].id

  const detailResponse = await getOrderDetail(orderId)

  console.log(detailResponse.data)
}

这个就很像正常业务流程:

text 复制代码
第一步
第二步
第三步

更容易维护。


11.6 async/await 怎么处理异常

Promise 使用:

js 复制代码
.catch()

而 async/await 一般搭配:

js 复制代码
try...catch

例如:

js 复制代码
async function loadUser() {
  try {
    const response = await getUser(1)

    console.log(response.data)
  } catch (error) {
    console.error(error)
  }
}

对应关系:

text 复制代码
Promise                   async/await

.then()                    await

.catch()                   try/catch

.finally()                 finally

例如:

js 复制代码
async getList() {
  this.loading = true

  try {
    const response = await listOrder(this.queryParams)

    this.orderList = response.rows
    this.total = response.total
  } catch (error) {
    console.error(error)
  } finally {
    this.loading = false
  }
}

这是非常标准的实际项目写法。


11.7 一个非常重要的问题:await 是不是会卡死页面?

不会。

比如:

js 复制代码
const response = await axios.get('/user')

并不是说:

JavaScript 整个线程什么都不干了。

而是当前这个:

js 复制代码
async function

暂停向下执行。

等 Promise 完成以后继续。

页面其它事件、渲染等仍然可以执行。

这个理解很重要。


11.8 串行和并行

这是 await 实战里很重要的一点。

比如两个接口互不依赖:

js 复制代码
const user = await getUser()
const config = await getConfig()

执行方式:

text 复制代码
getUser
↓
等待完成
↓
getConfig
↓
等待完成

属于串行。

如果两个接口完全没有依赖,可以:

js 复制代码
const [userResponse, configResponse] = await Promise.all([
  getUser(),
  getConfig()
])

执行方式:

text 复制代码
getUser   ───────→

getConfig ───────→

同时执行。

所以:

js 复制代码
Promise.all()

也是非常实用的东西。

例如:

js 复制代码
const [userRes, roleRes, deptRes] = await Promise.all([
  getUser(userId),
  getRoleList(),
  getDeptList()
])

比依次等待:

js 复制代码
const userRes = await getUser(userId)
const roleRes = await getRoleList()
const deptRes = await getDeptList()

更快。

前提是:

三个接口互不依赖。


12. class

ES6 增加了:

js 复制代码
class

用来创建类。

JavaScript:

js 复制代码
class User {
  constructor(name) {
    this.name = name
  }

  sayHello() {
    console.log(`hello ${this.name}`)
  }
}

使用:

js 复制代码
const user = new User('张三')

user.sayHello()

输出:

text 复制代码
hello 张三

12.1 constructor

js 复制代码
constructor(name) {
  this.name = name
}

你可以直接理解成 Java 的构造方法。

调用:

js 复制代码
new User('张三')

的时候自动执行。


12.2 实例属性

js 复制代码
class User {
  constructor(name, age) {
    this.name = name
    this.age = age
  }
}

创建:

js 复制代码
const user = new User('张三', 20)

那么:

js 复制代码
user.name
// 张三

user.age
// 20

12.3 实例方法

js 复制代码
class User {
  constructor(name) {
    this.name = name
  }

  sayHello() {
    console.log(`你好,我叫${this.name}`)
  }
}

调用:

js 复制代码
const user = new User('张三')

user.sayHello()

12.4 继承 extends

Java:

java 复制代码
class Admin extends User {
}

JavaScript 非常相似:

js 复制代码
class Admin extends User {
}

例如:

js 复制代码
class User {
  constructor(name) {
    this.name = name
  }

  sayHello() {
    console.log(`我是${this.name}`)
  }
}

class Admin extends User {
  deleteUser() {
    console.log('删除用户')
  }
}

使用:

js 复制代码
const admin = new Admin('管理员')

admin.sayHello()
admin.deleteUser()

12.5 super

如果子类也有构造方法:

js 复制代码
class Admin extends User {
  constructor(name, role) {
    super(name)

    this.role = role
  }
}

这里:

js 复制代码
super(name)

就是调用父类构造方法。


class 在前端实际使用多不多?

有,但不像:

text 复制代码
const
箭头函数
解构
数组方法
Promise
async/await

那么高频。

现在 Vue3 项目里,业务代码直接写 class 的情况并不算特别多。

但很多库和框架内部大量使用:

js 复制代码
class

例如:

js 复制代码
class ApiClient {
  constructor(baseURL) {
    this.baseURL = baseURL
  }

  get(url) {
    // ...
  }

  post(url, data) {
    // ...
  }
}

所以:

要会看、会写基础 class,但不用把它当目前最重点。


把 10~12 串起来

来看一个实际一点的例子。

js 复制代码
class UserService {
  async getUserDetail(userId) {
    try {
      const response = await getUser(userId)

      return response.data
    } catch (error) {
      console.error('查询用户失败', error)
      throw error
    }
  }
}

这里包含:

第 12 个

js 复制代码
class UserService

类。

第 11 个

js 复制代码
async getUserDetail()

异步函数。

第 11 个

js 复制代码
await getUser(userId)

等待 Promise。

第 10 个

js 复制代码
getUser(userId)

本质返回 Promise。

第 3 个也可以加入

例如:

js 复制代码
const { data } = await getUser(userId)

return data

这样你会发现 ES6 这些东西不是孤立的,而是在实际代码里混在一起使用。


目前 12 个知识点

现在整体结构已经很清楚了:

text 复制代码
1. let / const
   变量声明

2. 模板字符串
   `${}`

3. 解构赋值
   const { data } = response

4. 箭头函数
   item => item.id

5. 对象增强
   { name, age }

6. 展开运算符
   { ...user }

7. 剩余参数
   (...args)

8. 默认参数
   pageNum = 1

9. 数组方法
   map/filter/find/some/every

10. Promise
    异步结果

11. async / await
    更清晰地写 Promise

12. class
    类和继承

其中如果按你现在的 Vue / JavaScript 实战重要程度,我会把这一组排序为:

text 复制代码
★★★★★ async / await
★★★★★ Promise
★★★☆☆ class
相关推荐
冰暮流星1 小时前
javascript之webstorage用法
开发语言·javascript·ecmascript
执子念的飞鱼1 小时前
浏览器直接预览 Pages、Numbers、Keynote:iWork 格式真正难在哪
前端·javascript
alloc1 小时前
从延迟聚合到可解释诊断:MetricKit 的原理与工程化实践
前端
颜进强1 小时前
06 - OpenSpec change 从模糊想法到完整契约:new change / explore / propose 三连
前端·后端·ai编程
Cache技术分享1 小时前
499. Java 反射 - 获取类型上的注解
前端·后端
用户921080262861 小时前
左侧历史对话模块:使用 Conversations 搭建会话入口
前端
用户345208153881 小时前
我给 DSH 写了读 10x 数据的插件
javascript
用户69371750013841 小时前
9531 款 AI 工具流量真相:当 90% 的访问涌向 100 个平台,普通创业者还有机会吗?
前端·后端
莫问ABC2 小时前
HTML、CSS、JavaScript 前端三件套
前端·css·html
执子念的飞鱼2 小时前
70MB Excel 浏览器预览失败:定位 XLSX 重复解压与内存峰值
javascript·性能优化