JS非同步之Promise,Generator,Async
Promise
-
解決的問題:回撥地獄
-
Promise規範:
-
promise有三種狀態,等待(pending)、已完成(fulfilled/resolved)、已拒絕(rejected).Promise的狀態只能從“等待”轉到“完成”或者“拒絕”,不能逆向轉換,同時“完成”和“拒絕”也不能相互轉換.
-
promise 必須提供一個 then方法以訪問其當前值、終值和據因。promise.then(resolve, reject),resolve 和 reject都是可選引數。如果 resolve 或reject 不是函式,其必須被忽略.
-
then 方法必須返回一個 promise 物件.
-
使用:
-
例項化promise物件需要傳入函式(包含兩個引數),resolve和reject,內部確定狀態.resolve和reject函式可以傳入引數在回撥函式中使用.
resolve和reject都是函式,傳入的引數在then的回撥函式中接收.
var promise = new Promise(function(resolve, reject) {
setTimeout(function(){
resolve(`好哈哈哈哈`);
});
});
promise.then(function(val){
console.log(val)
})
-
then接收兩個函式,分別對應resolve和reject狀態的回撥,函式中接收例項化時傳入的引數.
promise.then(val=>{
//resolved
},reason=>{
//rejected
})
-
catch相當於.then(null, rejection)
當then中沒有傳入rejection時,錯誤會冒泡進入catch函式中,若傳入了rejection,則錯誤會被rejection捕獲,而且不會進入catch.此外,then中的回撥函式中發生的錯誤只會在下一級的then中被捕獲,不會影響該promise的狀態.
new Promise((resolve,reject)=>{
throw new Error(`錯誤`)
}).then(null,(err)=>{
console.log(err,1);//此處捕獲
}).catch((err)=>{
console.log(err,2);
});
// 對比
new Promise((resolve,reject)=>{
throw new Error(`錯誤`)
}).then(null,null).catch((err)=>{
console.log(err,2);//此處捕獲
});
// 錯誤示例
new Promise((resolve,reject)=>{
resolve(`正常`);
}).then((val)=>{
throw new Error(`回撥函式中錯誤`)
},(err)=>{
console.log(err,1);
}).then(null,(err)=>{
console.log(err,2);//此處捕獲,也可用catch
});
-
兩者不等價的情況:
此時,catch捕獲的並不是p1的錯誤,而是p2的錯誤,
p1().then(res=>{
return p2()//p2返回一個promise物件
}).catch(err=> console.log(err))
-
一個錯誤捕獲的錯誤用例:
該函式呼叫中即使發生了錯誤依然會進入then中的resolve的回撥函式,因為函式p1中例項化promise物件時已經呼叫了catch,若發生錯誤會進入catch中,此時會返回一個新的promise,因此即使發生錯誤依然會進入p1函式的then鏈中的resolve回撥函式.
function p1(val){
return new Promise((resolve,reject)=>{
if(val){
var len = val.length;//傳入null會發生錯誤,進入catch捕獲錯誤
resolve(len);
}else{
reject();
}
}).catch((err)=>{
console.log(err)
})
};
p1(null).then((len)=>{
console.log(len,`resolved`);
},()=>{
console.log(`rejected`);
}).catch((err)=>{
console.log(err,`catch`);
})
-
Promise回撥鏈:
-
promise能夠在回撥函式裡面使用 return 和 throw, 所以在then中可以return出一個promise物件或其他值,也可以throw出一個錯誤物件,但如果沒有return,將預設返回 undefined,那麼後面的then中的回撥引數接收到的將是undefined.
-
function p1(val){
return new Promise((resolve,reject)=>{
val==1?resolve(1):reject()
})
};
function p2(val){
return new Promise((resolve,reject)=>{
val==2?resolve(2):reject();
})
};
let promimse = new Promise(function(resolve,reject){
resolve(1)
})
.then(function(data1) {
return p1(data1)//如果去掉return,則返回undefined而不是p1的返回值,會導致報錯
})
.then(function(data2){
return p2(data2+1)
})
.then(res=>console.log(res))
Generator函式:
-
generator函式使用:
1、分段執行,可以暫停
2、可以控制階段和每個階段的返回值
3、可以知道是否執行到結尾
function* g() {
var o = 1;
yield o++;
yield o++;
}
var gen = g();
console.log(gen.next()); // Object {value: 1, done: false}
var xxx = g();
console.log(gen.next()); // Object {value: 2, done: false}
console.log(xxx.next()); // Object {value: 1, done: false}
console.log(gen.next()); // Object {value: undefined, done: true}
-
generator和非同步控制:
利用Generator函式的暫停執行的效果,可以把非同步操作寫在yield語句裡面,等到呼叫next方法時再往後執行。這實際上等同於不需要寫回撥函式了,因為非同步操作的後續操作可以放在yield語句下面,反正要等到呼叫next方法時再執行。所以,Generator函式的一個重要實際意義就是用來處理非同步操作,改寫回撥函式。
async和非同步:
-
用法:
-
async 表示這是一個async函式,await只能用在這個函式裡面。
-
await 表示在這裡等待非同步操作返回結果,再繼續執行。
-
await 後一般是一個promise物件
-
-
示例:async用於定義一個非同步函式,該函式返回一個Promise。
如果async函式返回的是一個同步的值,這個值將被包裝成一個理解resolve的Promise,等同於return Promise.resolve(value)。
await用於一個非同步操作之前,表示要“等待”這個非同步操作的返回值。await也可以用於一個同步的值。
let timer = async function timer(){
return new Promise((resolve,reject) => {
setTimeout(() => {
resolve(`500`);
},500);
});
}
timer().then(result => {
console.log(result); //500
}).catch(err => {
console.log(err.message);
});
//返回一個同步的值
let sayHi = async function sayHi(){
let hi = await `hello world`;
return hi; //等同於return Promise.resolve(hi);
}
sayHi().then(result => {
console.log(result);
});