前端系列-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); } ```

相关推荐
庸俗今天不摸鱼19 分钟前
【万字总结】前端全方位性能优化指南(十)——自适应优化系统、遗传算法调参、Service Worker智能降级方案
前端·性能优化·webassembly
QTX1873020 分钟前
JavaScript 中的原型链与继承
开发语言·javascript·原型模式
黄毛火烧雪下26 分钟前
React Context API 用于在组件树中共享全局状态
前端·javascript·react.js
Apifox37 分钟前
如何在 Apifox 中通过 CLI 运行包含云端数据库连接配置的测试场景
前端·后端·程序员
一张假钞40 分钟前
Firefox默认在新标签页打开收藏栏链接
前端·firefox
高达可以过山车不行40 分钟前
Firefox账号同步书签不一致(火狐浏览器书签同步不一致)
前端·firefox
m0_5937581041 分钟前
firefox 136.0.4版本离线安装MarkDown插件
前端·firefox
掘金一周44 分钟前
金石焕新程 >> 瓜分万元现金大奖征文活动即将回归 | 掘金一周 4.3
前端·人工智能·后端
三翼鸟数字化技术团队1 小时前
Vue自定义指令最佳实践教程
前端·vue.js
Jasmin Tin Wei2 小时前
蓝桥杯 web 学海无涯(axios、ecahrts)版本二
前端·蓝桥杯