Promise與async
主要內容:
- promise基本實現原理
- promise 使用中難點(鏈式調用,API基本上返回都是一個新Promise,及參數傳遞)
- promise 對異常處理
參考:
阮一峯ES6入門
0. 基本用法
基本的promise使用
1. 兼容性
查兼容性 基本上 主流瀏覽器支持沒有問題。
IE不兼容 問題,本文不予以處理,出門左轉,找谷哥。具體查看 babel,或者 自己實現一個Promise
2. ajax XMLHttpRequest封裝
//get 請求封裝
function get(url) {
// Return a new promise.
return new Promise(function(resolve, reject) {
// Do the usual XHR stuff
var req = new XMLHttpRequest();
req.open('GET', url);
req.onload = function() {
// This is called even on 404 etc
// so check the status
if (req.status == 200) {
// Resolve the promise with the response text
resolve(req.response);
}
else {
// Otherwise reject with the status text
// which will hopefully be a meaningful error
reject(Error(req.statusText));
}
};
// Handle network errors
req.onerror = function() {
reject(Error("Network Error"));
};
// Make the request
req.send();
});
}
1. Promse API
Promise API 分爲 :MDN
- 靜態方法
-
prototype
上方法Promise.prototype.then()
來分析
首先來看看 Promise.prototype.then()
返回一個Promise
,但Promise
內部有返回值,且 返回值,可以是個值,也可能就是一個新Promise
*具體規則如下:*
- *如果then中的回調函數返回一個值,那麼then返回的Promise將會成爲接受狀態,並且將返回的值作爲接受狀態的回調函數的參數值。*
- *如果then中的回調函數拋出一個錯誤,那麼then返回的Promise將會成爲拒絕狀態,並且將拋出的錯誤作爲拒絕狀態的回調函數的參數值。*
- *如果then中的回調函數返回一個已經是接受狀態的Promise,那麼then返回的Promise也會成爲接受狀態,並且將那個Promise的接受狀態的回調函數的參數值作爲該被返回的Promise的接受狀態回調函數的參數值。*
- *如果then中的回調函數返回一個已經是拒絕狀態的Promise,那麼then返回的Promise也會成爲拒絕狀態,並且將那個Promise的拒絕狀態的回調函數的參數值作爲該被返回的Promise的拒絕狀態回調函數的參數值。*
- *如果then中的回調函數返回一個未定狀態(pending)的Promise,那麼then返回Promise的狀態也是未定的,並且它的終態與那個Promise的終態相同;同時,它變爲終態時調用的回調函數參數與那個Promise變爲終態時的回調函數的參數是相同的。*
上面是官方規則,神馬,具體白話就是 核心是 返回參數及返回promise的狀態
參考:MDN
是不是 覺得很暈,沒關係,可以先看 下一節,看完後,再回過來看具體的說明
2. Prmise 鏈式調用
鏈式調用
1. 核心就是 then catch 等方法返回一個Promise 2. 鏈式 調用數據傳遞(注意)
1. 值傳遞問題
簡單例子
//正常狀態
const promise1 = new Promise((resolve, reject) => {
resolve('0000')//
})
promise1.then(result => {
console.log(result) //0000
return '1111';//類似於 return Promise.resolve('1111'); 參數是data,promise 狀態時 resolve
}).then(data => {
console.log(data) // 1111
})
一個實際的例子:(拿來大神的例子JavaScript Promise:簡介)
get('story.json').then(function(response) {
console.log("Success!", response);
})
//這裏的 response 是 JSON,但是我們當前收到的是其純文本。也可以設置XMLHttpRequest.responseType =json
get('story.json').then(function(response) {
return JSON.parse(response);
}).then(function(response) {
console.log("Yey JSON!", response);
})
//由於 JSON.parse() 採用單一參數並返回改變的值,因此我們可以將其簡化爲:
get('story.json').then(JSON.parse).then(function(response) {
console.log("Yey JSON!", response);
})
function getJSON(url) {
return get(url).then(JSON.parse);
}
//getJSON() 仍返回一個 promise,該 promise 獲取 URL 後將 response 解析爲 JSON。
2. 異步操作隊列
上面至今是return 值
,直接調用 下一下then
就OK了。
但如果return Promise
,則?
Promise.resolve(111).then(function(d){
console.log(d);
return Promise.resolve(d+111);//返回promise
}).then(function(d2){
console.log(d2);
})
// 111,222
3. 基本實現原理
主要是 如何自己實現一個簡單的Promise
//極簡實現
function Promise(fn) {
var value = null,
callbacks = []; //callbacks爲數組,因爲可能同時有很多個回調
this.then = function (onFulfilled) {
callbacks.push(onFulfilled);
};
function resolve(value) {
callbacks.forEach(function (callback) {
callback(value);
});
}
fn(resolve);
}
4. finnaly 實現
Promise.prototype.finally = function (callback) {
let P = this.constructor;
return this.then(
value => P.resolve(callback()).then(() => value),
reason => P.resolve(callback()).then(() => { throw reason })
);
};
5. 異常處理
異常分類:
- 同步異常
- 異步異常 無法
try-catch
得到- 多層Promise嵌套,獲異常取具體的一個promise異常,而不是全部
1. Promise 異常處理基本套路
基本處理異常中,有兩種方案then(undefined, func)
與catch()
但then(undefined, func)
與catch()
不同,具體參見代碼方案3
//方案1 使用 Promise.prototype.catch()來catch
const promise1 = new Promise((resolve, reject) => {
reject('no')//
})
promise1.then(result => {
console.log(result) // 永遠不會執行
}).catch(error => {
console.log(error) // no
})
//方案2 使用 Promise.prototype.then()中第二個參數 來處理
const promise1 = new Promise((resolve, reject) => {
reject('no')//
})
promise1.then(result => {
console.log(result) // 永遠不會執行
},error => {
console.log(error) // no
})
//方案2 (方案1 方案2 對比)
var promise2 = new Promise((resolve, reject) => {
resolve('yes')//
})
promise2.then(result => {
throw new Error('then');
console.log(result)
},error => {
console.log('1111',error) // no
}).catch(error=>{
console.log('2222',error)// 最終 err在此處被捕獲,而不是 then 中
})
2. 異常不同分類
Promise可能遇到的異常種類
//1.異常 reject()
const promise1 = new Promise((resolve, reject) => {
reject('no')//
})
promise1.then(result => {
console.log(result) // 永遠不會執行
}).catch(error => {
console.log(error) // no
})
//2.異常 顯示throw
const promise1 = new Promise((resolve, reject) => {
throw Error('no')
})
promise1.then(result => {
console.log(result) // 永遠不會執行
}).catch(error => {
console.log(error) //
})
//3.執行異常
const promise1 = new Promise((resolve, reject) => {
aaaa;
})
promise1.then(result => {
console.log(result) // 永遠不會執行
}).catch(error => {
console.log(error) //
})
3. 異常鏈式調用
asyncThing1().then(function() {
return asyncThing2();
}).then(function() {
return asyncThing3();
}).catch(function(err) {
return asyncRecovery1();
}).then(function() {
return asyncThing4();
}, function(err) {
return asyncRecovery2();
}).catch(function(err) {
console.log("Don't worry about it");
}).then(function() {
console.log("All done!");
})
上述代碼的流程圖形式:
// promise鏈式調用,catch住異常後,後面就不會處理異常了
Promise.reject().then(()=>{
console.log(2222);
},(err)=>{
console.log(333,err)
return err})
.catch((err)=>{
console.log(1111,err);
})
//333 undefined ,沒有打印 1111
//如果 在鏈式調用中,then 第二個參數 catch住了異常,沒有return Promise.reject()則後續鏈式調用返回rosolve狀態pormise
Promise.reject()
.then(()=>{
console.log(111);
},(err)=>{
console.log(111,err) //reject
return err;
}).then((data)=>{
console.log(222,data);//resolve 執行
},(err)=>{
console.log(222,err); //未執行
})
//4444 沒有執行 1111
4. 異常丟失
很多情況下,promise無法捕獲異常
場景1 macrotask 隊列中拋出異常:
//場景1
//永遠不要在 macrotask 隊列中拋出異常,因爲 macrotask 隊列脫離了運行上下文環境,異常無法被當前作用域捕獲。
function fetch(callback) {
return new Promise((resolve, reject) => {
setTimeout(() => {
throw Error('用戶不存在')
})
})
}
fetch().then(result => {
console.log('請求處理', result) // 永遠不會執行
}).catch(error => {
console.log('請求處理異常', error) // 永遠不會執行
})
// 程序崩潰
// Uncaught Error: 用戶不存在
/*
參考
作者:黃子毅
鏈接:https://www.jianshu.com/p/78dfb38ac3d7
來源:簡書
簡書著作權歸作者所有,任何形式的轉載都請聯繫作者獲得授權並註明出處。
*/
//解決場景1 怎麼解決,因爲setTimeout 是macrotask任務,執行上下文完全不同
/**
如何解決?
調用reject
*/
function fetch() {
return new Promise((resolve, reject) => {
setTimeout(() => {
reject('收斂一些')
})
})
}
fetch().then((resolve, reject) => {
console.log('resolve');
}).catch(error => {
console.log('捕獲異常', error) // 捕獲異常 收斂一些
})
場景二 Promise 狀態只能改變一次
//異常丟失
const promise2 = new Promise((resolve, reject) => {
reject('no')
console.log('reject after')
throw Error('no') //異常丟失
})
promise1.then(result => {
console.log(result) // 永遠不會執行
}).catch(error => {
console.log('err',error) // no
}).catch(error => {
console.log('err2',error) // 也無法捕獲異常
})
6.async
-
基本語法
function timeout(ms) { return new Promise((resolve) => { setTimeout(resolve, ms); }); } async function asyncPrint(value, ms) { await timeout(ms); console.log(value); return value; //類似 return Promise.resolve(value) } //async 返回一個promise asyncPrint('hello world', 50).then(function(d){ console.log('then',d); }); /** 打印 hello world then hello world */