普羅米修斯 Promise

鐵手小盆盆發表於2019-02-28

盜火的普羅米修斯

普羅米修斯 Promise
  曾經我在哪本書上看過這樣的介紹,Promise 的英文是從希臘語的直譯Prometheus演變過來,Promise也就是代表著先知 未來的意思。在希臘神話中,是最具智慧的神明之一,最早的泰坦巨神後代,名字有“先見之明”(Forethought)的意思。泰坦十二神伊阿佩託斯與名望女神克呂墨涅的兒子。普羅米修斯不僅創造了人類,給人類盜來了火,還教會了他們許多知識和技能。 後來由於給人類帶來了火種,他吩咐火神給普羅米修斯最嚴厲的懲罰。每天還要派一直鷹去啄他的肝臟,夜晚的時候肝臟又會重新長出來,夜以繼日的承受折磨。


故事結束,開始吹牛X

非同步函式 Promise

  我個人覺得Promise 函式跟 普羅米修斯很像,都是代表著處理未來的事情的先知。既然能處理未來的事情,那就代表著它擁有非常的能力,聽我慢慢吹來呀~ 首先來看看官方的吹牛文件解釋:

所謂Promise,簡單說就是一個容器,裡面儲存著某個未來才會結束的事件(通常是一個非同步操作)的結果。從語法上說,Promise 是一個物件,從它可以獲取非同步操作的訊息。Promise 提供統一的 API,各種非同步操作都可以用同樣的方法進行處理。

你看沒錯吧,是不是很像一個先知可以處理未來的事情?既然是神仙肯定是我們們凡人管不了的,為啥管不了那?就體現在它三個內部狀態上:

物件的狀態不受外界影響。Promise物件代表一個非同步操作,有三種狀態:pending(進行中)、fulfilled(已成功)和rejected(已失敗)只有非同步操作的結果,可以決定當前是哪一種狀態,任何其他操作都無法改變這個狀態。這也是Promise這個名字的由來,它的英語意思就是“承諾”,表示其他手段無法改變。

看見了嗎,敲黑板劃重點,神仙已經決定的東西就不能改變。而且一旦new Promise物件就不能取消,講究~

那如何使用那?下面來看一個簡單的例子:

const promise = new Promise (function(resolve, reject) { //這是先知啊,先知
  if (/* 非同步操作成功 */){
    resolve(value);  // resolve就是這事兒先知同意了,你就幹就完了
  } else {
    reject(error);  // reject就是這事兒先知不同意,白b扯了
  }
});
複製程式碼

注意上面的事情其實有沒有發生?沒有,先知嘛預知未來的事情,什麼時候發生那?當然先知同意的時候也就是說狀態變成resolved。只要一resolved 馬上就可以then了,劃重點 敲黑板 then 就是這玩意。

promise.then(function(value) {
  // 妥妥的這個已經辦了,下面你想咋地吧?可以連式操作無限then下去
}, function(error) {
  // 失敗了消停的把錯誤資訊打出來把
  console.log("錯誤資訊:"+error)
});
複製程式碼

那麼坑人的玩意來了,請看下面程式碼,誰先列印?

let promise = new Promise(function(resolve, reject) {
  console.log('Promise');
  resolve();
});

promise.then(function() {
  console.log('resolved.');
});

console.log('Hi!');
複製程式碼

上面程式碼中,Promise 新建後立即執行,所以首先輸出的是Promise。然後,then方法指定的回撥函式,將在當前指令碼所有同步任務執行完才會執行,所以resolved最後輸出。那麼接下來這段

普羅米修斯 Promise
你要是能直接寫出答案,基本Promise就不用看了,根據上面的理論,Promise一旦建立馬上就執行,但是我們們說了 resolve 是未來的狀態,所以第二行程式碼先等一等,三行程式碼也不用看了,肯定是四行程式碼先執行,然後往下走 第六行執行,回過頭來 第三行直接成功了,所以會接下去執行,最後一個不用我說了吧,第二行程式碼需要走個完成的resolve狀態,把1傳遞給第五行的t引數,所以肯定是最後執行的。


概念吹完了,來看勢力

妥了,接下來看一個用Promise物件實現的 Ajax 操作的例項:

const getJSON = function(url) { 
  const promise = new Promise(function(resolve, reject){
    const handler = function() {
      if (this.readyState !== 4) {
        return;
      }
      if (this.status === 200) {
        resolve(this.response);
      } else {
        reject(new Error(this.statusText));
      }
    };
    const client = new XMLHttpRequest();
    client.open("GET", url);
    client.onreadystatechange = handler;
    client.responseType = "json";
    client.setRequestHeader("Accept", "application/json");
    client.send();

  });

  return promise;
};

getJSON("/posts.json").then(function(json) {
  console.log('Contents: ' + json);
}, function(error) {
  console.error('出錯了', error);
});
複製程式碼

是不是很簡單,一點都不難,只是把ajax的操作流程放到了先知的內部,讓先知幫你獲取各種成功失敗的狀態!結合實際,我們在工作中可能一次要請求好幾個介面的資料,Promise提供了一個更加簡單的方法

const p = Promise.all([p1, p2, p3]);
複製程式碼

很好理解,all 的引數代表這一個可執行的任務佇列,只要裡面有任務就可以往下執行,也就是我們們訪問的多個api集合組成的陣列。

// 生成一個Promise物件的陣列
const promises = [2, 3, 5, 7, 11, 13].map(function (id) {
  return getJSON('/post/' + id + ".json"); //ID 作為引數 變成動態請求
});

Promise.all(promises).then(function (posts) {
  // ...
}).catch(function(reason){
  // ...
});
複製程式碼

還有一個類似 all的api Promise.race,Promise.race方法的引數與Promise.all方法一樣,如果不是 Promise 例項,就會先呼叫下面講到的Promise.resolve方法,將引數轉為 Promise 例項,再進一步處理

const p = Promise.race([
  fetch('/resource-that-may-take-a-while'),
  new Promise(function (resolve, reject) {
    setTimeout(() => reject(new Error('request timeout')), 5000)
  })
]);

p
.then(console.log)
.catch(console.error);
複製程式碼

上面程式碼中,如果 5 秒之內fetch方法無法返回結果,變數p的狀態就會變為rejected,從而觸發catch方法指定的回撥函式。

妥妥滴,既然到這裡我估計你應該懂了,其實任何技術都不難,都能拆分成若干個簡單點。任意簡單的點組合起來就又複雜了,正所謂大道至簡,相信萬事萬物都是簡單的原理,懷著敬畏的心理去學習,終究會得到自己的收穫。

相關文章