promise異步編程介紹
Promise 是什麼?
Promise 對象用來進行延遲(deferred) 和 異步(asynchronous) 計算。
解決的問題
回調函數是JavaScript的一大特色! node.js官方的api基本都是以會回調方式傳遞函數返回值。習慣同步編程的對這種異步方式多少會產生水土不服,而且層層嵌套,不知不覺就建造起了一座高高的回調金字塔。針對這種普遍問題,Promise應勢而生!
我們舉個例子
有以下的業務場景,從服務器中,依次請求三張圖片,每次有一張圖片加載完成,就打印結果。
一般正常的寫法
/**
* [loadimg 加載一張圖片]
* @param {[type]} url [description]
* @param {Function} callback [description]
* @return {[type]} [description]
*/
function loadimg(url, callback) {
var img = new Image();
img.src = url;
img.onload = callback;
}
//開始加載圖片
loadimg("img/a.png", function() {
console.log("圖片a已經加載完畢");
loadimg("img/b.png", function() {
console.log("圖片b已經加載完畢");
loadimg("img/c.png", function() {
console.log("圖片c已經加載完畢");
console.log("a,b,c三張圖片已經完全加載完畢");
});
});
});
ps:是不是很恐怖
採用promise的寫法
/**
* [loadimg 加載一張圖片]
* @param {[type]} url [description]
* @param {Function} callback [description]
* @return {[type]} [description]
*/
function loadimg(url) {
return new Promise(function(resolve, reject) {
var img = new Image();
img.src = url;
img.onload = function() {
resolve();
};
});
}
//開始加載圖片
loadimg("img/a.png").then(function() {
console.log("圖片a已經加載完畢");
return loadimg("img/b.png");
}).then(function() {
console.log("圖片b已經加載完畢");
return loadimg("img/c.png");
}).then(function() {
console.log("圖片c已經加載完畢");
console.log("a,b,c三張圖片已經完全加載完畢");
});
基本用法(then方法)
Promise/A+規範:
- 一個promise可能有三種狀態:等待(pending)、已完成(fulfilled)、已拒絕(rejected)
- 一個promise的狀態只可能從“等待”轉到“完成”態或者“拒絕”態,不能逆向轉換,同時“完成”態和“拒絕”態不能相互轉換
- Promise構造函數接受一個函數作爲參數,該函數的兩個參數分別是resolve方法和reject方法。如果異步操作成功,則用resolve方法將Promise對象的狀態變爲“成功”(即從pending變爲resolved);如果異步操作失敗,則用reject方法將狀態變爲“失敗”(即從pending變爲rejected)。
- promise必須實現
then
方法(可以說,then就是promise的核心),而且then必須返回一個promise,同一個promise的then可以調用多次,並且回調的執行順序跟它們被定義時的順序一致 - then方法接受兩個參數,第一個參數是成功時的回調,在promise由“等待”態轉換到“完成”態時調用,另一個是失敗時的回調,在promise由“等待”態轉換到“拒絕”態時調用。同時,then可以接受另一個promise傳入,也接受一個“類then”的對象或方法,即thenable對象。
創建Promise
var promise = new Promise(function(resolve, reject) {
// 做一些異步操作的事情,在這裏做了一個定時時間爲1000ms的定時器任務
setTimeout(function() {
if ( /* 一切正常 */ false) {
resolve("promise worked!");
} else {
reject("promise failed");
}
}, 1000);
});
Promise 的構造器接受一個函數作爲參數,它會傳遞給這個回調函數兩個變量 resolve 和 reject。在回調函數中做一些異步操作
成功之後調用對象的then方法,該方法中有兩個參數 1. resolve(val), val的返回成功的參數
否則調用 2.reject() //處理業務錯誤時的函數
獲取異步完成後的參數
promise.then(function(val) {
// success
console.log(val);
}, function(val) {
// failure
console.log(val);
});
promise catch 方法
catch 方法是 then(null,reject)中的reject處理的替代方法,用於指定發生錯誤時的回調函數
ps: promise對象的錯誤具有冒泡性質,會一直向後傳遞,直到被捕獲爲止
在這個例子中,創建一個Promise 發出一個異常消息
var promise = new Promise(function(resolve, reject) {
// resolve("ok");
reject("false"); //發出一個錯誤的異常消息
});
在這裏我們使用catch來捕獲錯誤
promise.then(function(str){
console.log("正確結果"+str);
}).catch(function(str){
console.log("異常結果"+str);
});
在這裏我們使用then(null,reject) 中的reject來捕獲錯誤
promise.then(function(str) {
console.log("正確結果" + str);
}, function(str) {
console.log("異常結果" + str);
});
all方法
Promise.all方法接受一個數組作爲參數,p1、p2、p3都是Promise對象的實例。(Promise.all方法的參數不一定是數組,但是必須具有iterator接口(可遍歷對象接口),且返回的每個成員都是Promise實例。)
p的狀態由p1、p2、p3決定,分成兩種情況。
(1)只有p1、p2、p3的狀態都變成fulfilled,p的狀態纔會變成fulfilled,此時p1、p2、p3的返回值組成一個數組,傳遞給p的回調函數。
(2)只要p1、p2、p3之中有一個被rejected,p的狀態就變成rejected,此時第一個被reject的實例的返回值,會傳遞給p的回調函數。
/**
* [createPromise 創建一個promise對象]
* @param {[type]} str [description]
* @return {[type]} [description]
*/
function createPromise(str) {
return new Promise(function(resolve, reject) {
setTimeout(function() {
if (true) {
resolve(str); //返回標誌位
} else {
reject();
}
}, 1000);
});
}
var promise1 = createPromise("p1"),
promise2 = createPromise("p2"),
promise3 = createPromise("p3");
//通過all方法將三個promise實例包裝成新的promise實例
var promise = Promise.all([promise1, promise2, promise3]);
promise.then(function(results) {
console.log(results); // ["p1", "p2", "p3"]
});
race方法
只要p1、p2、p3之中有一個實例率先改變狀態,p的狀態就跟着改變。那個率先改變的Promise實例的返回值,就傳遞給p的返回值。
/**
* [createPromise 創建一個promise對象,通過傳入不同的時間,來區別回調的時間]
* @param {[type]} str [description]
* @param {[type]} second [description]
* @return {[type]} [description]
*/
function createPromise(str, second) {
return new Promise(function(resolve, reject) {
setTimeout(function() {
if (true) {
resolve(str); //返回標誌位
} else {
reject();
}
}, second);
});
}
var promise1 = createPromise("p1", 5000),
promise2 = createPromise("p2", 1000),
promise3 = createPromise("p3", 2000);
//通過race方法將三個promise實例包裝成新的promise實例
var promise = Promise.race([promise1, promise2, promise3]);
promise.then(function(results) {
console.log(results); // "p2"
});
Promise.resolve方法 Promise.reject方法
有時需要將現有對象轉爲Promise對象,我們可以這樣做
var p = Promise.resolve('this is promise');
下面的代碼生成一個新的Promise對象的實例p,它的狀態爲fulfilled/rejected,所以回調函數會立即執行,Promise.resolve方法的參數就是回調函數的參數。
如果Promise.resolve方法的參數是一個Promise對象的實例,則會被原封不動地返回。
Promise.reject(reason)方法也會返回一個新的Promise實例,該實例的狀態爲rejected。Promise.reject方法的參數reason,會被傳遞給實例的回調函數。
var p = Promise.resolve('this is promise');
var p = Promise.reject('the promise is error');
p.then(function(result) {
console.log(result); //捕獲正常結果
}).catch(function(result) {
console.log(result); //捕獲錯誤結果
});
兼容不支持promise的環境
在html中引入
<script src="https://www.promisejs.org/polyfills/promise-7.0.4.min.js"></script>