axios原理之promise
axios 是基于 promise 的 HTTP 库,支持 promise 的所有 API,9.1 节和 9.2 节只讲解了怎样使用 axios,本节讲解 promise,掌握 promise 更有利于我们理解 axios。
为什么要用promise
promise 支持链式调用,可以解决回调地狱。
什么是回调地狱?
回调地狱涉及多个异步操作,多个回调函数嵌套使用。例如有3个异步操作,第2个异步操作是以第1个异步操作的结果为条件的,同理第3个异步操作是以第2个异步操作的结果为条件的。
此时出现了回调函数嵌套,代码将向右侧延伸,不便于阅读,也不便于异常处理,使用 promise 首先能解决回调函数嵌套问题,代码从上往下执行,更有利于代码阅读及代码异常处理。
promise基本使用
首先需要创建 promise 对象并传入回调函数,promise 的基本使用代码如下。
const p = new Promise((resolve, reject) => {
//执行异步操作
setTimeout(() => {
const time = Date.now();
//当前时间为偶数代表成功,当前时间为奇数代表失败
if (time % 2 == 0) {
//执行成功调用resolve(value)
resolve('成功的数据:' + time);
} else {
//执行失败调用reject(reason)
reject('失败的数据:' + time);
}
}, 1000);
});
p.then(
value => {
//接收成功的value数据
console.log('接收成功的数据---' + value);
},
reason => {
//接收失败的reason数据
console.log('接收失败的数据---' + reason);
}
);
代码解析如下。
(1)new Promise()实例对象要传入回调函数,回调函数有两个形参,分别是resolve和reject。 (2)在回调函数中执行异步操作,在此案例中是获取当前时间,如果当前时间为偶数,表示异步操作执行成功,如果当前时间为奇数,表示异步操作执行失败。 (3)如果当前时间为偶数,则调用成功的resolve()方法,方法中的参数就是需要展示的数据。 (4)如果当前时间为奇数,则调用失败的reject()方法,方法中的参数就是需要展示的数据。 (5)使用.then()方法获取成功或失败的数据,如果获取数据成功,则调用第一个回调函数,如果获取数据失败,则调用第二个回调函数,最终的运行结果如图9-2所示。

promise的API
下面介绍 promise 的常用 API。
Promise (excutor) {}
(1)excutor()函数:同步执行(resolve, reject) ⇒ {}。 (2)resolve()函数:定义成功时调用的函数value ⇒ {}。 (3)reject()函数:定义失败时调用的函数reason ⇒ {}。
executor 会在 promise 内部立即同步回调,异步操作则在执行器中执行。 |
Promise.prototype.then()方法:(onResolved, onRejected) ⇒ {}
(1)onResolved()函数:成功的回调函数为(value) ⇒ {}。 (2)onRejected()函数:失败的回调函数为(reason) ⇒ {}。
Promise.prototype.catch()方法:(onRejected) ⇒ {}
onRejected() 函数:失败的回调函数为(reason) ⇒ {}。 代码展示如下。
new Promise((resolve, reject) => {
// 模拟异步操作
setTimeout(() => {
// 异步操作成功
resolve('成功的数据');
// 异步操作失败
reject('失败的数据');
}, 1000);
}).then(
value => {
// 获取成功的数据
console.log(value);
}
).catch(
reason => {
// 获取失败的数据
console.log(reason);
}
);
Promise.resolve()方法:(value) ⇒ {}
value:成功的数据。
案例:定义成功值为1的promise对象。
1)方法一
const p1 = new Promise((resolve, reject) => {
resolve(1);
});
p1.then(value => {
console.log(value);
});
2)方法二
const p2 = Promise.resolve(1);
p2.then(value => {
console.log(value);
});
Promise.resolve 可以更加简洁、方便地定义成功的 promise 对象。 |
Promise.reject()方法:(reason) ⇒ {}
reason:失败的原因。
案例:定义失败值为 2 的 promise 对象。
1)方法一
const p3 = new Promise((resolve, reject) => {
reject(2);
});
p3.then(
null,
reason => {
console.log(reason);
}
);
2)方法二
const p4 = Promise.reject(2);
p4.then(null, reason => {
console.log(reason);
});
|
或者使用 .catch()
方法获取,代码如下。
const p4 = Promise.reject(2);
p4.catch(reason => {
console.log(reason);
});
Promise.all()方法:(promises) ⇒ {}
promises:多个 promise 数组。
作用:返回一个新的 promise 对象,当数组中的所有 promise 对象都执行成功,才为成功,只要数组中有一个 promise 对象执行失败,就为失败,代码如下。
const p1 = new Promise((resolve, reject) => {
//成功
resolve(1);
});
//成功
const p2 = Promise.resolve(1);
//失败
const p3 = Promise.reject(2);
const pAll = Promise.all([p1, p2, p3]);
pAll.then(
values => {
console.log(values);
},
reason => {
console.log(reason);
}
);
代码解析如下。
因为 p3 是错误的数据,所以运行上述代码,最终会执行 reason 回调函数,执行结果就是 p3 中定义的错误数据 “2”,如图 9-3 所示。

如果把 p3 删掉,此时 p1 和 p2 都是定义成功的 promise 对象,所以 pAll.then() 会执行成功的回调函数。
执行结果会以数组的形式打印,运行结果如图 9-4 所示。 |

async与await
当前,async 与 await 是编写异步操作的解决方案,也是建立在 promise 基础上的新写法,两者同时使用,如果在方法中使用了 await,那么方法前面必须加上 async。
async函数
作用:返回promise对象。
async 的右侧是一个函数,函数的返回值是 promise 对象。 |
const p1 = new Promise((resolve, reject) => {
//成功
resolve(1);
});
//成功
const p2 = Promise.resolve(2);
//失败
const p3 = Promise.reject(3);
const pRace = Promise.race([p1, p2, p3]);
pRace.then(
value => {
console.log(value);
},
reason => {
console.log(reason);
}
);
代码解析如下。
如果 fn1() 函数前面不加 async,毫无疑问,res 的打印结果应该是 hello async,加上 async 之后将打印 promise 对象,如图 9-5 所示。

既然返回的是 promise 对象,获取数据就要使用 .then
方法,并且要设置成功的回调函数和失败的回调函数,代码如下。
async function fn1() {
return "hello async";
}
const res = fn1();
res.then(
value => {
console.log(value);
},
reason => {
console.log(reason);
}
);
运行代码,此时在 .then
中执行的是成功的回调函数,如图 9-6 所示。

加上 async 之后,返回的是 promise 对象,我们也可以直接在 fn1 函数中设置返回失败的数据,代码如下。
async function fn1() {
return Promise.reject('失败的数据');
}
const res = fn1();
res.then(
value => {
console.log(value);
},
reason => {
console.log(reason); // 输出:失败的数据
}
);
运行结果如图 9-7 所示。

通过上述案例,可以得出两个结论。
(1)async函数的返回值为promise对象。 (2)promise对象的结果,由async函数执行的返回值决定。
await表达式
作用:等待 async 函数执行完成,并返回 async 函数成功的值,代码如下。
async function fn1() {
return 'Hello async';
}
async function fn2() {
const res = await fn1();
console.log(res);
}
fn2();
代码解析如下。
fn1() 函数的返回值是 promise 对象。在 fn2() 函数中,await 获取到的是 hello async,表示使用 await 可以直接获取到 promise 对象成功的值,不需要使用 .then()
方法,运行结果如图 9-8 所示。

await 必须写在 async 函数中,但 async 函数中可以没有 await。 |
扩展
fn1() 函数的返回值是 promise,并且设置的是成功的数据。那么如果 fn1() 函数返回的是失败的数据,上述代码可以正常运行吗?代码如下。
|
运行结果如图 9-9 所示。

结论:await 只能等待 promise 对象返回成功的数据,如果 promise 返回的是失败的数据,直接使用 await 则会报错,应该使用 try/catch 获取失败结果,最终代码如下。
async function fn1() {
return Promise.reject('失败数据');
}
async function fn2() {
try {
const res = await fn1();
console.log(res);
} catch (error) {
console.log(error); // 输出:失败数据
}
}
fn2();