promise基礎
- Promise 是ES6中新增的一個類,專門用來解決異步回調地獄的問題,將異步代碼同步顯示出來;
- 回調地獄,promise,把異步以同步顯示出來;
- promise 的回調函數是同步的,then對應的函數是異步的;
- Promise 是ES6中新增的一個類,專門用來解決異步回調地獄的問題,將異步代碼同步顯示出來
- promise 三個狀態: pending 進行中 fulfilled 成功 rejected 失敗
- 改變狀態有兩種可能 pending–>fulfilled pending–> rejected
let p = new Promise(function (resolve, reject) {
resolve(); // 調用resolve代表執then中的第一個回調,resolve代表的是成功態
// 調用reject ,就代表執行then的的第二個回調;,失敗態
reject();
}).then(function () {
console.log(1);
}, function () {
console.log(2);
}).then(function () {
}, function () {
})
setTimeout(function(){
console.log("買菜");
setTimeout(function(){
console.log("洗菜");
setTimeout(function(){
console.log("做菜");
},2000);
},2000);
},3000)
let p = new Promise(function(resolve,reject){
setTimeout(function(){
console.log("買菜");
resolve();
},3000)
}).then(function(){
return new Promise(function(resolve,reject){
setTimeout(function(){
console.log("洗菜");
resolve()
},2000)
})
}).then(function(){
return new Promise(function(resolve,reject){
setTimeout(function(){
console.log("做菜");
resolve()
},2000)
})
})
Promise.prototype.then()
- then : 在Promise的原型上的一個方法
- p.then 也會默認返回一個promise實例,而且默認是成功的;如果在then的回調函數中也返回一個promise實例,那麼這個promise實例就會覆蓋默認返回的實例影響下一個then中的回調函數;
- 如果return promise的實例那麼下面的then就會受實例的影響,在實例執行完後再執行then 如果沒有return promise的實例那麼下面的then就不受影響,直接執行
<body>
<h2>需求:promise執行買菜做飯過程</h2>
<br>
1.買菜
<br>
2.洗菜
<br>
3.做飯
<br>
4.喫飯
<br>
</body>
<script>
let p = new Promise(function (resolve, reject) {
setTimeout(function () {
if (Math.random() > 0.5) {
resolve(100);
} else {
reject();
}
}, 1000)
})
// then : 在Promise的原型上
// p.then 也會默認返回一個promise實例,而且默認是成功的;
如果在then的回調函數中也返回一個promise實例,那麼這個promise實例就會影響下一個then中的回調函數;
p.then(function (a) {
console.log(a);
return new Promise(function (resolve, reject) {
resolve();
});
}, function () {
console.log("不買");
}).then(function () {
// 第二個then的方法受第一個then返回值影響;
console.log("成功")
}, function () {
console.log("失敗")
})
</script>
function getJSON(url) {
let p = new Promise(function (resolve,reject) {
let xhr = new XMLHttpRequest;
xhr.open('get',url,true);
xhr.responseType = 'json';
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && /^2\d{2}$/.test(xhr.status)) {
resolve(JSON.parse(xhr.responseText));
}
}
xhr.send();
})
return p;
}
getJSON('data.json').then(function (data) {
//受上面異步請求的時間控制;當異步請求成功以後,執行這個回調函數
return getJSON('1.txt')
},function () {
}).then(function () {
//這個then受第一個then中返回的新的promise實例的影響
})
Promise.prototype.catch()
- 一般來說,不要在then中定義第二個reject函數;一般使用catch;理由是第二種寫法可以捕獲前面then方法執行中的錯誤,也更接近同步的寫法(try/catch)。
- 如果在then中拋出錯誤,就會執行下面的catch
- 如果then中有一個異常,也會拋出錯誤
- 只要then鏈中有一個異常,就會執行最後的catch;只要在then中有失敗的回調,就不再執行最後的catch了
- 只要其中一個then的回調失敗,就不再執行下面的then中的回調,而是直接走catch
let p = new Promise(function (resolve,reject) {
resolve();
})
p.then(function () {
console.log(1);
console.log(a);
}).then(function () {
}).then(function () {
}).catch(function () {
console.log(2);
})
//顯示 1 2
Promise.prototype.finally()
- promise 的finally 是不管成功還是失敗,都會執行的回調
- 這是會等到promise實例狀態改變以後會觸發的回調,是一個異步的;
let p = new Promise(function(resolve,reject){
resolve();
})
p.then(function(){
console.log(1);
console.log(a);
}).catch(function(){
console.log(55);
}).finally(function(){
// 這是會等到promise實例狀態改變以後會觸發的回調,是一個異步的;
console.log(1000);
})
console.log(999);
Promise.all()
- Promise.all : 用於多個實例,返回一個新promise實例
- 全部成功狀態纔是成功,只要有一個失敗狀態就是失敗
- 如果不是 Promise 實例,就會先調用下面講到的
Promise.resolve()
方法,將參數轉爲 Promise 實例,再進一步處理。
let p1 = new Promise(function(resolve,reject){
setTimeout(function(){
reject(100);
},3000)
})
let p2= new Promise(function(resolve,reject){
setTimeout(function(){
reject(200);
},2000)
})
let p3= new Promise(function(resolve,reject){
setTimeout(function(){
resolve(300);
},4000)
});
let p = Promise.all([p1,p2,p3]) //全部執行完纔會返回結果
p.then(function(data){
// 會將所有成功的數據組裝一個新數組,傳遞給這個回調函數
console.log(data);
}).catch(function(data){
// 只要遇到一個失敗,就將失敗的這個值傳遞給這個回調
console.log(data);
})
Promise.race()
- Promise.race()` 方法同樣是將多個 Promise 實例,包裝成一個新的 Promise 實例。
Promise.race()
方法的參數與Promise.all()
方法一樣,如果不是 Promise 實例,就會先調用下面講到的Promise.resolve()
方法,將參數轉爲 Promise 實例,再進一步處理。
let p1 = new Promise(function(resolve,reject){
setTimeout(function(){
reject(100);
},13000)
})
let p2= new Promise(function(resolve,reject){
setTimeout(function(){
reject(200);
},12000)
})
let p3= new Promise(function(resolve,reject){
setTimeout(function(){
resolve(300);
},4000)
});
let p = Promise.race([p1,p2,p3])
p.then(function(){
console.log("成功");
}).catch(function(){
console.log("失敗");
})
//誰先執行返回的結果是啥p的結果就是啥
上面代碼中,只要p1、p2、p3之中有一個實例率先改變狀態,p的狀態就跟着改變。
那個率先改變的 Promise 實例的返回值,就傳遞給p的回調函數。
Promise.resolve()
- Promise.resolve: 將一個對象轉成一個promise的實例;返回值可以調用then方法
Promise.resolve("foo").then(function(){
})
new Promise(function(resolve,reject){
resolve("foo")
});
let p = Promise.resolve();
p.then(function(){
})
let a = {
then:function(){
}
}
Promise封裝
<script>
// 重寫Promise;寫一個類似於Promise這個類的方法
class MyPromise {
constructor(excutor) { // 當new MyPromise,constructor執行的;
// this --> Promise的實例;
// pending fulfilled rejected
this.state = "pending";
// 用來存儲成功的回調函數和失敗的回調函數的;
this.fulfilledEvent = [];
this.rejectedEvent = [];
// 1.resolve,改變了狀態
let resolve = (result) => {
// 執行resolve,那麼實例的狀態變成了成功態;this-->Promise實例
// 如果不是pending狀態,就不再向下執行;
if (this.state !== "pending") return;
this.state = "fulfilled";
clearTimeout(this.timer);
this.timer = setTimeout(() => {
this.fulfilledEvent.forEach(item => {
if (typeof item == "function") {
item(result);
}
})
})
};
let reject = (result) => {
if (this.state !== "pending") return;
this.state = "rejected";
clearTimeout(this.timer);
this.timer = setTimeout(() => {
this.rejectedEvent.forEach(item => {
if (typeof item == "function") {
item(result);
}
})
})
}
try {
excutor(resolve, reject);
} catch (e) {
// e: 錯誤信息
reject(e);
}
}
// 是訂閱;是在往成功的事件池和失敗的事件池放入成功的回調函數和失敗的回調函數
then(resolveFn, rejectedFn) {
// 當then不傳回調時,給兩個形參賦默認值
if (resolveFn === undefined) {
resolveFn = () => {}
}
if (rejectedFn === undefined) {
rejectedFn = () => {}
}
// this
return new MyPromise((resolve, reject) => { // then 返回的這個實例是p2;
// 往事件池中放入方法;
// resolve :這個是函數,函數中的this--> p2;
// this--> then返回的promise實例
// this.fulfilledEvent.push(resolveFn);
this.fulfilledEvent.push((result) => {
try {
let x = resolveFn(result);
// resolve : 讓成功的事件池中的方法運行,裏面執行時,
this-->P2的事件池中的方法執行
x instanceof MyPromise ? x.then(resolve, reject) : resolve();
} catch (e) {
reject(e);
}
});
this.rejectedEvent.push((result) => {
try {
let x = rejectedFn(result);
// resolve : 讓成功的事件池中的方法運行,裏面執行時,
this-->P2的事件池中的方法執行
x instanceof MyPromise ? x.then(resolve, reject) : resolve();
} catch (e) {
reject(e);
}
});
})
}
}
let p1 = new MyPromise(function (resolve, reject) {
// resolve(100)
// resolve(200);
// reject();
// console.log(a);
// console.log(resolve);
resolve();
})
p1.then(function (a) {
// fn1
// 成功的回調函數
console.log(a);
return new Promise(function (resolve, reject) { //:x===p3
resolve
(); // 這個resolve 執行,能讓p3的事件池中的方法執行,
p3的事件池中有個resolve;所以就會這個p3事件中的resolve執行,p3中的resolve執行,
這個this指向p2,那麼就能讓p2事件池中的方法運行;
})
}, function () {
// fn2
// 失敗的回調
console.log(88);
}).then(function () {
// fn3
// 如果上一個then中的回調返回一個promise實例,那麼這個函數會被resolve包裹一下,
再放進這個實例的事件池中
}, function () {
})
//console.log(p1);
// p1 p2 p3
// 第一個then:把方法放到p1的事件池中;
// 第二個then:把方法放到p2的事件池中;
//
let p = new Promise(function (resolve, reject) { // resolve : 形參
// 如果代碼異常,會走失敗;不會在控制檯拋出異常
//console.log(a);
resolve();
})
let c = p.then(function () {
//
}).then(function () {
// 上一個中的回調如果不返回promise實例,
那麼這個then受上一個then默認返回的promise的狀態影響,如果回調中返回了promise實例,
那麼這個then就受返回的promise實例的影響
}, function () {
});
console.log(c);
</script>
<script>
class MyPromise{
constructor(excutor){
this.state = "pending";// 當前實例有個state,默認值是pending;
// 當初始化promise實例時,新增兩個事件池;一個成功一個失敗;
this.fulfilledEvent = [];
this.rejectedEvent=[];
// resolve : 1. 改變狀態;2.讓成功事件池中的方法都執行;
let resolve=(result)=>{
// 如果不是pending狀態,那麼promise狀態已經發生了改變,不需要再執行;
if(this.state!=="pending")return;
this.state="fulfilled";
clearTimeout(this.timer);
this.timer = setTimeout(()=>{
// 事件池中的方法執行是一個異步的;
this.fulfilledEvent.forEach(item=>{
if(typeof item==="function"){
item(result)
}
})
},0)
};
let reject = (result)=>{
if(this.state!=="pending")return;
this.state="rejected";
clearTimeout(this.timer);
this.timer = setTimeout(()=>{
this.rejectedEvent.forEach(item=>{
if(typeof item==="function"){
item(result)
}
})
},0)
};
try{
excutor(resolve,reject)
}catch(e){
reject(e);
}
}
// 訂閱方法;
then(resolveFn,rejectFn){
//如果then不傳參數;給函數賦默認的匿名函數;
if(resolveFn===undefined){
resolveFn=()=>{};
}
if(rejectFn===undefined){
rejectFn=()=>{
throw new Error();
}
}
return new MyPromise((resolve,reject)=>{// p1 p1中的resolve--> this指向p1;
// 必須驗證resolveFn是否是一個promise的實例
this.fulfilledEvent.push((result)=>{
try{
let x = resolveFn(result);
// x===p2 resolve 存儲到了p2的事件池中;
x instanceof MyPromise?x.then(resolve,reject):resolve();
}catch(e){
reject(e);
}
});
this.rejectedEvent.push((result)=>{
try{
let x = rejectFn(result);
// x===p2 resolve 存儲到了p2的事件池中;
x instanceof MyPromise?x.then(resolve,reject):resolve();
}catch(e){
reject(e)
}
});
})
}
}
let p = new MyPromise(function (resolve,reject) {
setTimeout(function () {
resolve(1)
},2000)
})
p.then(function (val) {
/*return new MyPromise(function (resolve,reject) {
setTimeout(function () {
resolve(2);
},2000)
})*/
},function () {
}).then(
function () {
}
)
/*let p = new MyPromise(function (resolve,reject) {
// resolve形參可以執行;所以該實參一定是個函數
// resolve : 改變當前實例的狀態
resolve()
});
p.then(function () {
return new MyPromise(function (resolve,reject) { // p2
resolve();// 執行p2事件池中的方法;p2事件池中有個p1 的resolve,p1的resolve執行;
會把p1成功事件池中的方法執行;
})
}).then(function () {
});*/
// 第一個resolve 要執行p這個實例中成功事件池中的方法;這是一個異步的操作;
暫時異步事件池中的方法是不執行;
// 第二個resolve : 執行p2事件池中的方法;p2事件池中有個p1 的resolve,p1的resolve執行;
會把p1成功事件池中的方法執行;
// 第一個then : 把then中的方法放入到p的成功事件池中;並且返回一個p1的promise實例;
// 第二個then :p1.調用的then;把then中放到了p1.的成功事件池中;
/* let p1 = new Promise(function (resolve) {
resolve()
})
p1.then(function () {
// then : 向事件池中存儲方法;
})
p1.then(function () {
})
console.log(p1);*/
/*class A{
constructor(num){
// this--> 實例
this.num = num;
}
// 原型上的方法
then(){
console.log(this);
}
}
let a = new A(100);
a.then()*/
</script>
Promise深入
一、什麼是promise
ES6 語法規範中新加的內置類,用來處理 js 中異步編程的,而我們所謂的 Promise 設計模式,就是基於 promise 對異步操作進行管理
promose 是一個內置類,所以創建一個 promise:new Promise([executor]): 第一個執行函數必須傳遞,這裏的 executor 是一個回調函數下面簡稱 exe
-
new promise 的時候就會把 exe 執行,創建 promise 的一個實例(exe 是 promise 類的一個回調函數,promise 內部會把它執行)
-
promise 不僅把 exe 執行,而且還給 exe 傳遞兩個參數(兩個參數也是函數類型)
resolve 函數:它執行代表 promise 處理的異步事情是成功的,把 promise 的狀態改爲 fulfilled
reject 函數:它執行代表 promise 處理的異步事情是失敗的,把 promise 的狀態改爲 rejected -
exe 函數中放的就是當前要處理的異步操作事情
let promiseExamp = new Promise((resolve, reject) => {這裏一般存放的都是我們即將要處理的異步任務,任務成功我們執行 resolve,任務失敗我們執行 reject(當然寫同步的也可以)}
promise執行具體細節
舉個例子:
let ran = Math.random();
setTimeout(() => {
if (ran < 0.5) {
reject(ran);
return;
}
resolve(ran);
}, 1000);
promiseExamp.then(result => {
// 狀態爲 FULFILLED 成功後執行(RESULT:[[PromiseValue]])
console.log('成功: ' + result);
}, error => {
// 狀態爲REJECTED 失敗後執行
console.log('失敗: ' + error);
});
二、異步編程中的回調地獄
一個回調函數嵌一個回調函數再嵌一個回調函數再嵌一個回調函數再嵌一個回調函數再嵌一個回調函數再嵌一個回調函數再嵌一個回調函數再嵌一個回調函數再嵌一個回調函數再嵌一個回調函數再嵌一個回調函數再嵌一個回調函數再嵌一個回調函數再嵌一個回調函數再嵌一個回調函數再嵌一個回調函數再嵌一個回調函數再嵌一個回調函數再嵌一個回調函數再嵌一個回調函數再嵌一個回調函數再嵌一個回調函數再嵌一個回調函數再嵌一個回調函數再嵌一個回調函數… 就是回調地獄。
一個小例子:
從服務器獲取某個學生的基本信息 (score-id) -> 基於分數的 ID 獲取到當前學生各項成績 -> 基於某一項成績獲取他在學校的排名
1. AJAX 的串行
只有第一個請求成功才能執行第二個,第二個成功才能執行第三個,最後一個請求成功後拿到了每一次請求的所有數據。
$.ajax({
url:'/baseInfo',
method:'GET',
data:{
name:'zhanglu'
},
success:result => {
let scoreId = result.scoreId;
$.ajax({
url:'/scoreInfo',
method:'GET',
data:{
id:scoreId
},
success:result => {
let chinese = result.chinese;
$.ajax({
url:'/paiming',
method:'GET',
data:{
num:chinese
},
success:result => {
}
});
}
});
}
});
2. AJAX 的並行
三個請求可以同時發送,但是需要等到所有請求都成功纔會做一件事
let chi = 100,
eng = 12,
math = 98;
let chiPai,
engPai,
mathPai;
let count = 0;
function func () {
if(count >= 3) {
// 處理自己要做的事情
}
}
$.ajax({
url:'/pai ? chi = ' + chi,
success:result => {
chiPai = result;
count++;
func();
}
});
$.ajax({
url:'/pai ? eng = ' + eng,
success:result => {
engPai = result;
count++;
func();
}
});
$.ajax({
url:'/pai ? math = ' + math,
success:result => {
mathPai = result;
count++;
func();
}
});
3. 解決AJAX的回調地獄
function queryBase () {
return new Promise (resolve => {
$.ajax({
url: '/baseInfo?name=zhanglu',
success: result => {
resolve(result);
}
});
});
}
function queryScore (scoreId) {
return new Promise(resolve => {
$.ajax({
url: '/score?id=' + scoreId,
success: result => {
resolve(result);
}
});
});
}
function queryChinese (chinese) {
return new Promise (resolve => {
$.ajax({
url: '/paiming?chin=' + chinese,
success: result => {
resolve(result);
}
});
});
}
4. 解決AJAX的並行
function ajax1 () {
return new Promise (resolve => {
$.ajax({
url: '/api1',
success: resolve
});
});
}
function ajax2 () {
return new Promise (resolve => {
$.ajax({
url: '/api2',
success: resolve
});
});
}
function ajax3 () {
return new Promise (resolve => {
$.ajax({
url: '/api3',
success: resolve
});
});
}
三、then
1. 什麼是then
Promise.prototype 上面有 then 的方法
- then: 設置成功或者失敗後執行的方法(成功或者失敗都可以設置,也可以只設置一個)
pro.then ([success],[error])
pro.then ([success],null)
pro.then (null,[error])
-
catch: 設置失敗後執行的方法
-
finally: 設置不論成功還是失敗都會執行的方法(一般不用)
new Promise((resolve, reject) => {
// resolve(100); // 把第一個 promise 實例的 value 值改爲100 / -100
reject(-100);
}).then(result => {
console.log(result);
return result * 10; // then 中 return 的結果相當於把當前這個新的 promise 實例中的 value 值
// 改爲返回值
}, err => {
console.log(err);
return err / 10;
}).then(A => {
console.log('A:' + A);
}, B => {
console.log('B:' + B);
}).then(C => {
}, D => {
});
2. then鏈
-
執行 then / catch / finally 返回的結果是一個全新的 promise 實例,所以可以鏈式寫下去;下一個 then 中哪個方式會被執行,由上一個 then 中某個方法執行的結果來決定
-
上一個 then 中某個方法的返回值會傳遞給下一個 then 的某個方法中
-
如果當前 promise 實例的狀態確定後,都會到對應的 then 中找方法,如果 then 中沒有對應的這個方法,則會向下順延
-
then 方法中如果返回的是一個 promise 實例,則當前返回實例的成功或者失敗狀態,影響着下一個 then 中哪個方法會被觸發執行;如果返回的是非 promise 實例,則看當前方法執行是否報錯,來決定下一個 then 中哪個方法執行;
new Promise((resolve, reject) => {
resolve();
}).then().catch(x => {
console.log(1);
}).then(x => {
console.log(2); // OK
}).then(x => {
console.log(3); // OK
}).catch(x => {
console.log(4);
}).then(x => {
console.log('AAA'); // OK
console.log(AAA); // 報錯
}).catch().then(null, x => {
console.log(5); // OK
});
3. promise.all
-
Promise.all([promise1, promise2,…]):all 中存放的是多個 promise 實例(每一個實例管理者一個異步操作),執行 all 方法返回的結果是一個新的 promise 實例 “PROA”
-
當所有 promise 實例的狀態都爲 Fulfilled 的時候(成功),讓 PROA 的狀態也變爲 Fulfilled,並且把所有 promise 成功獲取的結果,存儲爲成爲一個數組(順序和最開始編寫的順序一致)“result=[result1,result2,…]”,讓 PROA 這個數組的 value 值等於這個數組
-
都成功(PROA 狀態是 fulfilled)纔會通知 then 中第一個方法執行,只要有一個失敗(PROA 狀態是 recected),就會通知 then 中第二個方法或者 catch 中的方法執行
Promise.all([ajax1(), ajax3(), ajax2()]).then(results => {
// results:[result1, result3, result2]
});
Promise.race([ajax1(), ajax3(), ajax2()]).then(result => {
// 看哪一個 promise 狀態最先處理完(成功或者失敗),以最先處理完的爲主
});