從Promise到異步函數(async,await)
Promise
Promise
出現的目的是解決js
異步編程中回調地獄的問題。
Promise
本身沒有提供新的功能,它只是異步編程中語法上的改進。
Promise
是一個構造函數,如果你需要使用Promise
就需要new
一個Promise
實例對象。
語法示例
let promise = new Promise((resolve, reject) => {
setTimeout(() => {
if (true){
resolve('異步API的執行結果');
}else{
reject('異步API的失敗執行結果');
}
}, 2000)
});
// promise允許鏈式調用
promise.then(result => console.log(result)) //異步API的執行結果
.catch(error => console.log(error)); //異步API的失敗執行結果
- resolve(函數)
- reject(函數)
- then(方法)
- catch(方法)
用Promise解決回調地獄
- 出現了一個異步API的執行需要上一個異步API的執行結果的問題,我們用定時器來代替異步API演示
let a = 0;
setTimeout(() => {
let b = a + 1;
console.log(b);
setTimeout(() => {
let c = b + 1;
console.log(c);
setTimeout(() => {
let d = c + 1;
console.log(d);
}, 2000)
}, 2000)
}, 2000)
// 用Promise解決回調地獄,先用promise把每一個‘異步函數封裝’
function p1() {
return new Promise((resolve, reject) => {
setTimeout(() => {
let b = a + 1;
resolve(b);
reject('p1失敗了');
}, 2000)
});
}
function p2(b) {
return new Promise((resolve, reject) => {
setTimeout(() => {
let c = b + 1;
resolve(c);
reject('p2失敗了');
}, 2000)
});
}
function p3(c) {
return new Promise((resolve, reject) => {
setTimeout(() => {
let d = c + 1;
resolve(d);
reject('p3失敗了');
}, 2000)
});
}
//然后再鏈式調用.then和.catch方法,並且將下一個需要調用的函數return回去,並且將參數傳遞
p1().then((r1) => {console.log(r1); return p2(r1);}) //1
.then((r2) => {console.log(r2); return p3(r2);}) //2
.then((r3) => {console.log(r3);}) //3
.catch((err) => {console.log(err)}); //當出現失敗的情況時,使用catch()中斷,並且失敗結果會一層一層傳到最下面的catch()
異步函數
- 異步函數是異步編程語法的中終極解決方案,它可以讓我們將異步代碼寫成同步的形式,讓代碼不再有回調函數嵌套,是代碼變得更加清晰。
語法
- 在普通函數定義的前面加上async關鍵字 普通函數就變成了異步函數
- 異步函數默認的返回值是promise對象
- 在異步函數內部使用return關鍵字進行結果返回,結果會被包裹在promise對象中,return關鍵字代替了resolve方法
- 在異步函數內部使用throw關鍵字拋出程序異常
- 調用異步函數在鏈式調用then()方法獲取函數執行結果
- 調用異步函數再鏈式調用catch()方法獲取函數執行的錯誤信息
const fn = async() => {};
async function fn() {};
async function fn() {
return '執行成功'; //resolve
throw '執行失敗'; //reject
}
fn().then((res) => {console.log(res)}) //執行成功
.catch((err) => {console.log(err)}); //執行失敗
用異步函數解決回調地獄
- await關鍵字
- 它只能出現在異步函數中
- await后面要跟promise對象,寫其它類型的API是不可以的
- 它可以暫停異步函數的執行,等待promise對象返回結果再向下執行函數
let a = 0;
function p1() {
return new Promise((resolve, reject) => {
setTimeout(() => {
let b = a + 1;
resolve(b);
reject('p1失敗了');
}, 2000)
});
}
function p2(b) {
return new Promise((resolve, reject) => {
setTimeout(() => {
let c = b + 1;
resolve(c);
reject('p2失敗了');
}, 2000)
});
}
function p3(c) {
return new Promise((resolve, reject) => {
setTimeout(() => {
let d = c + 1;
resolve(d);
reject('p3失敗了');
}, 2000)
});
}
async function run() {
try{
let r1 = await p1()
console.log(r1); //1
let r2 = await p2(r1);
console.log(r2); //2
let r3 = await p3(r2);
console.log(r3); //3
}catch(e) {
console.log(e);//捕獲錯誤
}
}
run();
promise對象的進一步處理
function p() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('成功');
reject('失敗');
}, 2000)
});
}
// 模仿ajax進一步處理promise對象
function pp() {
return p().then(res => {
// 處理的代碼 res
return res;
// return的值會作為Promise對象下一個then的回調函數的參數值
})
}
// 或者
async function pp() {
const res = await p();
// 處理 res
return res;
}
async function run() {
const res = await pp();
}
結尾
- 在實際開發中如果有一個異步函數的執行需要另一個異步函數的執行結果,我們就可以將異步請求API封裝成promise方法,然后在用async和await將起寫成同步代碼的樣式,簡化代碼並且方便閱讀。