Javascript异步转同步篇一——Promise简介

6,231 阅读3分钟

这是我参与11月更文挑战的第27天,活动详情查看:2021最后一次更文挑战

今天本来准备继续更新PageHelper源码解读系列文章,奈何笔记本不再身边,今天说点别的内容:前段的异步同步问题,先来说说Promise吧。

vue项目中常见的api请求方式

熟悉vue的小伙伴一定不会对vue-element-admin感到陌生,这个框架页基本是若依管理系统ruoyi-ui部分的基础框架,我们对到底是谁先按照这种前端规范模式来写的也不太感兴趣。

如果真正的使用过vue-element-ui或者若依系统的小伙伴页一定不会对其中的API请求方式感到陌生,其请求方式大致如下:封装axios.requst,在src/api文件夹下按照如下方式执行:

import request from '../utils/request';
// post方法
export function post_api(query) => {
    return request({
        path: '/api-url',
        method: 'post',
        data
    })
}

// get方法
export function get_api(query) {
  return request({
    url: '/api-url',
    method: 'get',
    params: query
  })
}

这里返回的request(xxx)是个什么呢?

Promise

答案是一个Promise。那么Promise是什么呢?

可以简单理解为目前Javascript实现异步编程的强大工具。

Promise有三种状态:Pending(进行中)/ Fulfilled(已成功)/ Rejected(已失败),如下面代码所示:

// 代码A
new Promise((resolve, reject) => {
    if(xx) {
        resolve(xxxx)
    } else {
        reject(new Error("xxx"))
    }
})
// 代码B

这段代码首先会执行代码A,然后执行new Promiseb部分,不管promise的状态如何,也会立即执行代码B部分代码。

那么如何让Promise部分代码执行完毕后再执行B代码呢?按照下面这样写:

// 代码A
new Promise((resolve, reject) => {
    if(xx) {
        resolve(xxxx)
    } else {
        reject(new Error("xxx"))
    }
}).then(() => {
    // 代码B
}).catch(() => {
    // 代码C
}).finally(() => {
    // 代码D
})

这段代码表示若Promise部分以resolve后返回,即“已成功”,这执行then部分代码;若“已失败”返回则执行catch部分代码;而且无论成功与失败,finally部分代码总要执行。

一种生产环境中使用finally的场景是,某些后台接口可能成功,也可能会因为超时的情况发生失败,这种情况下,一些统一的请求后处理需要放在finally部分,例如将loading状态改为false等。

this.loading = true
api.request(...)
    .then(...)
    .catch(...)
    .finally(() => {
        this.loading = false
    })

题外话

Javascript中还有两个异步同步的关键字,await/async,有过java编程经验的小伙伴应该不会感到陌生。这两个关键字也是为Promise服务的。async用来修饰function,await用于在async修改的function内部使用,两者互相关联使用才会生效。

使用async声明的function返回的总是一个promise。即如果一个函数使用async修饰,我们便不能再使用常规方式获取其返回值,而是需要像Promise一样取值,例如:

async demo() {
    return "哈哈"
}

demo().then(val => {
    console.log(val) // 哈哈
})

await,顾名思义,等待Promise的执行结束。

例如:在MDN上的一个例子:

function resolveAfter2Seconds(x) {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve(x);
    }, 2000);
  });
}

async function f1() {
  var x = await resolveAfter2Seconds(10);
  console.log(x); // 10
}
f1();