像結識了一位新朋友一樣,最近開始學習了promise。一開始接觸到這個名詞的時候也在腦海中想,promise是到底是做什麼的呢。那麼下面跟我一起來認識一下promise把。
首先來了解一下promise的含義
簡單來說,promise是一種非同步流程的控制手段,比傳統的回撥函式解決方案更合理和簡潔。它解決了傳統回撥函式方式產生的回撥地獄的問題。ES6將其寫進了語言標準,統一了語法,原生提供了Promise。
下面我們來看一個簡單的例子:
有這麼一個場景,想做一道菜:首先要去買菜,買完菜之後才能洗菜,洗完菜之後才能切菜,切完之後才能炒菜,轉換成js如下:
function A(callback){
setTimeout(function(){
callback("菜買好了");
}, 1000);
}
function B(callback){
setTimeout(function(){
callback("菜洗好了");
}, 2000);
}
function C(callback){
setTimeout(function(){
callback("菜切好了");
}, 3000);
}
function D(callback){
setTimeout(function(){
callback("菜炒好了");
}, 3000);
}
複製程式碼
- 傳統的回撥函式方式是
A(function(res){
console.log(res);
B(function(res){
console.log(res);
C(function(res){
console.log(res);
C(function(res){
console.log(res);
console.log("菜炒好啦");
});
});
});
});
複製程式碼
這就是傳說中的回撥地獄,實際的場景巢狀的層級可能還更多。這種程式碼非常不易於理解,而且程式碼難以維護。 而promise就非常適合這種場景,上面的程式碼用promise實現
function A() {
return new Promise(function (resolve, reject) {
setTimeout(function(){
resolve("菜買好了");
}, 1000);
});
}
function B() {
return new Promise(function (resolve, reject) {
setTimeout(function(){
resolve("菜洗好了");
}, 2000);
});
}
function C() {
return new Promise(function (resolve, reject) {
setTimeout(function(){
resolve("菜切好了");
}, 2000);
});
}
function D() {
return new Promise(function (resolve, reject) {
setTimeout(function(){
resolve("菜炒好了");
}, 2000);
});
}
A().then(function(res) {
console.log(res);
return B();
}).then(function(res) {
console.log(res);
return C();
}).then(function(res) {
console.log(res);
return D();
}).then(function(res) {
console.log(res);
console.log("菜炒好了");
});
複製程式碼
可見Promise最大的好處是在非同步執行的流程中,把執行程式碼和處理結果的程式碼清晰地分離了。
深入理解promise
Promise物件可以理解為一次將要執行的操作(常常被用於非同步操作),使用了 Promise 物件之後可以用一種鏈式呼叫的方式來組織程式碼,讓程式碼更加直觀。promise 實現鏈式呼叫返回的並不是this而是一個新的promise。
promise(承諾)有三種狀態,分別是
- resolve (成功)
- reject (失敗)
- pending (等待態)
狀態改變: Promise物件的狀態改變,只有兩種可能: 從pending變為reject。 從pending變為resolve 如果一旦promise成功了就不能失敗,相反也是一樣的。所以這兩種情況只要發生,狀態就凝固了,不會再變了,這時就稱為resolved(已定型)。
每一個promise的例項上都有一個then方法,then方法中有兩個引數,一個引數叫成功的函式 ,一個是失敗的函式。 then方法定義:then(fulfilledAHandler, errorHandler),對應著Promise的resolve, reject。
一個promise可以then多次
let p = new Promise((resolve,reject)=>{
resolve('成功');
});
p.then(data=>{
console.log(data);
});
p.then(data=>{
console.log(data);
})
p.then(data => {
console.log(data);
});
複製程式碼
promise中只要發生錯誤,就會執行失敗態。如果返回的是一個普通值就會走到寫一個then中的成功回撥,如果有錯誤產生會走失敗的回撥。
- Promise只有一個引數 叫excutor執行器,預設new時就會呼叫
let p = new Promise((resolve,reject)=>{
// 預設promise中的executor是同步執行的
resolve('買');
});
// then方法是非同步呼叫的,事件環
p.then(
(value)=>{ // value成功
console.log(1);
},
(err)=>{ // err失敗
});
console.log(2);
複製程式碼
執行結果,2 1。
promise可以處理併發任務
promise用promise.all方法做併發呼叫,當所有Promise 物件都變為完成態或失敗態時,回撥將被執行。
let fs = require('fs'); // fileSystem
function read(url) {
return new Promise((resolve, reject) => {
fs.readFile(url, 'utf8', function (err, data) {
if (err) reject(err);
resolve(data);
})
})
}
Promise.all([read('1.txt'),read('2.txt')]).then((data)=>{
console.log(data);
},err=>{
console.log(err);
});
複製程式碼
promise.race賽跑,處理多請求只取最快的
Promise.all([read('1.txt'),read('2.txt')]).then((data)=>{
console.log(data);
},err=>{
console.log(err);
});
複製程式碼
讀取哪個檔案快,返回哪個檔案的結果
Promise.resolve() 返回一個成功的promise
Promise.reject() 返回一個失敗的promise
promise.reject('123').then(null,data=>{
console.log(data);
});
複製程式碼
最後用es6語法簡單實現一下Promise的原理
class Promise {
constructor(executor) {
this.status = 'pending';
this.value = undefined;
this.reason = undefined;
let resolve = value => {
if (this.status ==='pending'){
this.status = 'resolved';
this.value = value;
}
}
let reject = reason => {
if (this.status === 'pending') {
this.status = 'rejected';
this.reason = reason;
}
}
try {
executor(resolve, reject);
} catch (e) {
reject(e);
}
}
then(onFufilled,onRjected){
if(this.status === 'resolved'){
onFufilled(this.value);
}
if(this.status === 'rejected'){
onRjected(this.reason);
}
}
}
module.exports = Promise
複製程式碼