下面用到模块化的相关知识,如果不是很了解,可以参考另一篇文章:
https://blog.csdn.net/zxd1435513775/article/details/106334253
1. Promise的基本介绍Promise
在ES6中被列为正式规范,是ES6中最重要的特性之一。
下面在Chrome的控制台中,输入console.dir(Promise)
,见下图:
从上图中,可以看出
- Promise是一个构造函数,自己身上有all、reject、resolve这几个方法
- 原型上有then、catch等方法
- 用Promise new出来的对象肯定就有then、catch方法
简单使用
// 1. 创建一个新的promise对象
const p = new Promise(function(resolve, reject) {// 执行器函数 同步回调
console.log('执行 excutor')
// 2. 执行异步操作任务
setTimeout(() => {
const time = Date.now() // 如果当前时间是偶数就代表成功, 否则代表失败
// 3.1. 如果成功了, 调用resolve(value)
if (time %2 == 0) {
resolve('成功的数据, time=' + time)
} else {
// 3.2. 如果失败了, 调用reject(reason)
reject('失败的数据, time=' + time)
}
}, 1000);
})
console.log('new Promise()之后')
p.then(
value => { // 接收得到成功的value数据
console.log('成功的回调', value)
},
reason => {// 接收得到失败的reason数据
console.log('失败的回调', reason)
}
)
p.catch(() => {
console.log('catch...')
})
- 实例化
Promise
时有两个回调函数,resolve、reject,成功执行resolve,失败执行reject - 在实例化p的then中有两个对应的回调函数,第一个回调是resolve执行时触发,第二个回调是reject执行时触发
- p.then()返回是Promise类型,p.catch()返回是Promise类型
then方法可以接受两个参数,第一个对应resolve的回调,第二个对应reject的回调,能够分别拿到它们传过来的数据
我们知道Promise
对象除了then方法,还有一个catch方法,它是做什么用的呢?
其实它和then的第二个参数一样,用来指定reject的回调,用法是这样:
p.then(
function (value) {
// 接收得到成功的value数据
console.log('成功的回调', value)
//此处输入一个未定义的变量,如果没有catch(),则会报错,后面都不会执行,卡死在这
console.log(ssss)
console.log('成功的回调11', value)
},
function (reason) {
console.log('失败的回调', reason)
}
)
p.catch((reason) => {
console.log('catch...')
})
效果和写在then的第二个参数里面一样。不过它还有另外一个作用:在执行resolve的回调(也就是上面then中的第一个参数)时,如果抛出异常了(代码出错了),那么并不会报错卡死js,而是会进到这个catch方法中。
2. Promise的初封装import axios from 'axios';
let baseURL; // process.env.NODE_ENV环境
if(process.env.NODE_ENV=='development'){
baseURL = 'http://127.0.0.1:3000/api'
}else{
baseURL = '/xxx'
}
const $http = axios.create({
baseURL,
})
// 分别暴露
export const get = (url,params)=>{
params = params || {};
return new Promise((resolve,reject)=>{
// axiso 自带 get 和 post 方法
$http.get(url,{
params,
}).then(res=>{
if(res.data.status===0){
resolve(res.data);
}else{
alert(res.data.msg)
}
}).catch(error=>{
alert('网络异常');
})
})
}
export const post = (url,params)=>{
params = params || {};
return new Promise((resolve,reject)=>{
$http.post(url,params).then(res=>{
if(res.data.status===0){
resolve(res.data);
}else{
alert(res.data.msg);
}
}).catch(error=>{
alert('网络异常');
})
})
}
如何引用呢?
import { get, post } from "./utils/index";
Vue.prototype.$http = {
get,
post
};
这里使用了构造函数的原型prototype声明一个全局变量,并且把封装好的get和post方法放在里面
3. Promise再封装 3.1 第一版封装目录结构
├── common
│ ├── api
│ │ └── index.js
├── main.js
main.js
import App from './App'
import api from "./common/api" // 导入/common/api/index.js文件
import Vue from 'vue'
Vue.config.productionTip = false
Vue.prototype.$api = api
App.mpType = 'app'
const app = new Vue({
...App
})
app.$mount()
index.js
const getLabel = (data) => {
return new Promise((resolve,reject)=>{
// 此处可以换成其他任何发送请求的方式,如:ajax,axios都可以的
uniCloud.callFunction({
name:'getLabel',
data
}).then((res)=>{
if(res.result.code === 200){
resolve(res.result)
}else{
reject(res.result)
}
}).catch((err)=>{
reject(err)
})
})
}
// 默认导出
export default{
getLabel
}
在其他地方就可以使用如下方式调用getLabel()
这个方法了:
this.$api.getLabel().then((res)=>{
console.log('getLabel',res)
})
上面这个版本,是将所有的请求方法都放在index.js这一个文件里,随着方法的增多,这个文件肯定会越来,不易于维护。下面就可以按模块,将各个模块的请求方法放到自己的文件中,然后在index.js这个文件中进行汇总,来看看吧。
3.2 第二版封装目录结构如下:
├── common
│ ├── api
│ │ └── index.js
│ │ └── list.js
├── main.js
list.js
// 使用分别暴露
export const getLabel = (data) => {
return new Promise((resolve,reject)=>{
uniCloud.callFunction({
name:'getLabel',
data
}).then((res)=>{
if(res.result.code === 200){
resolve(res.result)
}else{
reject(res.result)
}
}).catch((err)=>{
reject(err)
})
})
}
export const getList = (data) => {
return new Promise((resolve,reject)=>{
resolve({data:"请求成功"})
})
}
index.js
// 在index.js中引入list.js,再暴露出来,其他模块也可以这样的方式引入
import {getLabel,getList} from './list.js';
export default{
getLabel,
getList
}
使用
methods: {
getLabel(){
// 调用请求
this.$api.getList().then((res)=>{
console.log('getList',res)
})
this.$api.getLabel().then((res)=>{
console.log('getLabel',res)
})
}
}
3.3 第三版封装
对于第二个版本,按模块去封装对应的请求方法,这个是没问题的,但是需要手动的在index.js文件中引入,是不是这一步也是有点多余呢?下面就来在index.js中自动引入所有的模块请求函数。
目录结构:
├── common
│ ├── api
│ │ └── index.js
│ │ └── list.js
│ ├── http.js
├── main.js
index.js
// 批量导出文件
const requireApi = require.context(
// api 目录的相对路径
'.',
// 是否查询子目录
false,
// 查询文件的一个后缀
/.js$/
)
let module = {}
// console.log(requireApi.keys()) 当前目录下的所有文件
requireApi.keys().forEach((key,index)=>{
if(key === './index.js') return
// console.log(key);
// console.log('requireApi(key)',requireApi(key))
Object.assign(module,requireApi(key))
})
// console.log('module',module)
export default module
把上面的注释打开,输出是这样的:
list.js
import $http from '../http.js'
export const getLabel = (data) => {
return $http({
url: 'getLabel',
data
})
}
export const getList = (data) => {
return $http({
url: 'getList',
data
})
}
http.js
export default function $http(options) {
// 结构赋值
const { url, data } = options
return new Promise((reslove, reject) => {
uniCloud.callFunction({
name: url,
data
}).then((res) => {
if (res.result.code === 200) {
reslove(res.result)
} else {
reject(res.result)
}
}).catch((err) => {
reject(err)
})
})
}