前端系列-4 promise与async/await与fetch/axios使用方式

背景:

本文介绍promise使用方式,以及以Promise为基础的async/await用法和fetch/axios使用方式,主要以案例的方式进行。

1.promise

1.1 promise介绍

javascript是单线程执行的,异步编程的本质是事件机制和函数回调。当执行阻塞任务或者进行IO操作时,编程时很容易嵌套多层回调,严重影响可读性和维护性,陷入地狱回调,如下所示:

js 复制代码
var addStudent = function (name, callback) {
    setTimeout(function () {
        console.log("add " + name + " success");
        callback();
    }, 1000);
};
addStudent("student-1", function () {
    addStudent("student-2", function () {
        addStudent("student-3", function () {
            console.log("end");
        });
    });
});

执行结果如下所示:

js 复制代码
VM153:3 add student-1 success
VM153:3 add student-2 success
VM153:3 add student-3 success
VM153:10 end

promise被用于解决该问题而被引入, 使用Promise的写法:

js 复制代码
function addStudentPromise(name) {  
    return new Promise((resolve) => {  
        setTimeout(function () {  
            console.log("add " + name + " success");  
            resolve();
        }, 1000);  
    });  
}  
  
// 使用.then()链来链接异步操作  
addStudentPromise("student-1")  
    .then(() => addStudentPromise("student-2"))  
    .then(() => addStudentPromise("student-3"))  
    .then(() => console.log("end"));

1.2 promise用法

promise有三种状态, pending-进行中、fulfilled-已成功、rejected-已失败; 对应两种状态切换路径: pending-> fulfilled 和 pending-> rejected。需要注意的是Promise在状态切换后不会再发生状态变化。
[1] 创建Promise

以下是一个Promise对象创建的模板:

js 复制代码
new Promise((resolve, reject) => {
    // 代码A
});  

Promise创建时, 会立即执行代码A(一般包括异步逻辑),此时Promise处于pending状态;当代码A内部的resolve方法被调用后,Promise转为fulfilled状态;当代码A内部的rejected方法被调用后,Promise转为rejected状态。

js 复制代码
new Promise((resolve, reject) => {
    setTimeout(function () {resolve("success");}, 1000);
}); 

new Promise((resolve, reject) => {
    setTimeout(function () {reject("failed");}, 1000);
}); 

[2] 编写异步逻辑

js 复制代码
let ps = new Promise((resolve, reject) => {
    setTimeout(function () {resolve("success");}, 1000);
}); 

ps.then((data)=>{
 console.info("result is: " + data)
}).catch((data)=>{
 console.info("exception is: " + data)
})

异步逻辑通过then和catch完成,promise对象转为(或处于)fulfilled时,执行then逻辑;promise对象转为(或处于)rejected时,执行catch逻辑。参数data分别是调用resolve和reject方法时的入参。

[3] 案例介绍

js 复制代码
function generateOddNumber(){
 let ps = new Promise(function(resolve, reject){
  setTimeout(function(){
   var num = Math.ceil(Math.random() * 100);
   if(num % 2 == 1){
    resolve('[Success] Odd is:' + num);
   }
   else{
    reject('[Failed] Even is:' + num);
   }
  }, 1000);
 })
   return ps;
}

generateOddNumber()
.then(
 function(data){
  console.log(data);
 }
)
.catch(function(data){
 console.log(data);
}); 

运行结果如下所示:

复制代码
[Success] Odd is:73

说明:由于Promise创建时会立即执行传入的函数,一般将Promise的创建放在函数内部,调用函数时才会创建Promise对象。基于Promise的特点,常在then中返回Promise对象以形成链式回调。除此之外,Promise支持通过all和race将多个Promise绑定在一起执行,all是所有Promise均完成而race表示至少一个Promise完成。

2.async和await

async与await的组合是一种基于Promise实现的异步编程方法。

async写在方法前用于标记异步方法,js会异步执行该方法(本质上是一个Promise对象包裹的函数),即不会因该方法阻塞而阻塞整体的执行流程。

await必须用在aync标记的方法内,表示等待逻辑执行完成,await会阻塞方法内后续的逻辑。

使用案例如下所示:

js 复制代码
function generateFruit() {
    return new Promise((resolve, reject) => {
        setTimeout(() => resolve('apple'), 10000);
    })
}

async function test() {
    console.log("test begin")
    // await此时会等待异步逻辑generateFruit执行完成,才会继续执行下一行代码
    let result = await generateFruit()
    console.log("test end, result is " + result)
    console.log("test end")
}

console.log("main begin")
// test是async方法,不会阻塞下一行运行
test()
console.log("main end")

执行结果如下:

js 复制代码
main begin
VM142:8 test begin
VM142:16 main end
VM142:10 test end, result is apple
VM142:11 test end

3.fetch

fetch方法用于代替jquery的ajax实现http请求调用,其位于浏览器的window对象中,因此不需要添加依赖。

使用方法较为简单,如案例所示:

js 复制代码
fetch("http://127.0.0.1:8182/test/api/createFruite",
    {
        mode: 'cors',
        method: "POST",
        headers: {
            "Content-Type": "application/json"
        },
        body: JSON.stringify({
            "id": 123456789,
            "name": "apple"
        })
    })
    .then(function (res) {
        if (res.ok) {
            console.log(res.body);
            console.log("call http success");
        }
    })
    .catch(function (e) {
        console.log("call http failed")
    });

fetch方法会返回一个Promise对象,因此可通过then和catch开发异步逻辑。

4.axios

axios是基于Promise对原生 XMLHttpRequest 的封装以及增强,如提供了拦截器功能。
安装axios:

shell 复制代码
 npm install axios

在使用的地方通过import导入:

js 复制代码
import axios from 'axios'

axios用法:

可通过axios提供的get/put/post等方法,也通过配置项method指定HTTP方法类型:

js 复制代码
#axios的get方法
axios.get('http://127.0.0.1:8182/test/api/queryFruite', {
    params: {
        id: 1
    }
}).then(res => {
    console.log(res);
}, err => {
    console.log(err);
})

#在axios的配置项method中指定
axios({
    method: 'get',
    url: 'http://127.0.0.1:8182/test/api/queryFruite',
    params: {
        id: 1
    }
}).then(res => {
    console.log(res);
}, err => {
    console.log(err);
})

常见的axios的配置项有

1\] url 请求路径 \[2\] method 方法 \[3\] headers 请求头 \[4\] params 请求参数(追加到url后) \[5\] data 请求消息体 \[6\] timeout 请求超时时长(单位ms) 一般,对同域请求写在一个js中,可以基于baseURL创建一个公共的axios实例对象,如下所示: ```js const apiClient = axios.create({ baseURL: 'http://127.0.0.1:8182/test', headers: { timeout: 5000 } }) const listLocalModels = async function () { const response = await apiClient.get('/queryFruite') return response.data } const showModelInformation = async function (request) { const response = await apiClient.post('/createFruite', request) return response.data } ``` **拦截器使用方式:** 拦截器使用如下所示: 在根目录下创建文件".env": ```shell VITE_API_URL=http://127.0.0.1:8182/test ``` 定义一个axios实例,在该实例上设置拦截器: ```javascript import axios from 'axios' const instance = axios.create({ baseURL: `${import.meta.env.VITE_API_URL}/api`, timeout: 5000 }); // 请求拦截器 instance.interceptors.request.use( config => { //config.headers['Authorization'] = 'Bearer ' + localStorage.getItem('token'); return config; }, error => { return Promise.reject(error); } ); // 响应拦截器 instance.interceptors.response.use( response => { //... return response.data; }, error => { return Promise.reject(error); } ); // 封装 get 请求 export async function get(url, params = {}) { return await instance.get(url, { params }); } // 封装 post 请求 export function post(url, data = {}) { return instance.post(url, data); } ```

相关推荐
每天都有好果汁吃10 分钟前
基于 react-use 的 useIdle:业务场景下的用户空闲检测解决方案
前端·javascript·react.js
穗余14 分钟前
NodeJS全栈开发面试题讲解——P10微服务架构(Node.js + 多服务协作)
前端·面试·node.js
横冲直撞de30 分钟前
前端下载文件,文件打不开的问题记录
前端
占星安啦33 分钟前
一个html实现数据库自定义查询
java·前端·javascript·数据库·动态查询
love530love36 分钟前
Windows 下部署 SUNA 项目:虚拟环境尝试与最终方案
前端·人工智能·windows·后端·docker·rust·开源
凌晨作案1 小时前
ck-editor5的研究 (5):优化-页面离开时提醒保存,顺便了解一下 Editor的生命周期 和 6大编辑器类型
前端·ckeditor5
天天扭码1 小时前
面试必备 | React项目的一些优化方案(持续更新......)
前端·react.js·面试
老K(郭云开)1 小时前
allWebPlugin中间件VLC专用版之截图功能介绍
前端·javascript·chrome·中间件·edge
Rousson2 小时前
硬件学习笔记--65 MCU的RAM及FLash简介
开发语言·前端·javascript
萌萌哒草头将军2 小时前
🏖️ TanStack Router:搜索参数即状态!🚀🚀🚀
javascript·vue.js·react.js