你好,promise

淺淺藍qsl發表於2018-05-20

像結識了一位新朋友一樣,最近開始學習了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
複製程式碼