javascript非同步與promise

陌上寒發表於2019-01-21

同期非同步系列文章推薦
談一談javascript非同步
javascript非同步中的回撥
javascript非同步之Promise.all()、Promise.race()、Promise.finally()
javascript非同步之Promise.resolve()、Promise.reject()
javascript非同步之Promise then和catch
javascript非同步之async(一)
javascript非同步之async(二)
javascript非同步實戰
javascript非同步總結歸檔

我們說處理javascript非同步最常用的方式就是通過回撥函式,對於回撥函式我們昨天對此做了介紹
簡單快速,
我們一般使用巢狀回撥或者鏈式回撥,會產生以下問題

  1. 當採用巢狀回撥時,會導致層級太多,不利於維護
  2. 所以我們又採用了鏈式回撥,對巢狀回撥進行拆分,拆分後的函式間耦合度很高,
  3. 如果需要傳遞引數,函式之間的關聯性會更高,而且要對引數進行校驗以提高程式碼的健壯性
  4. 如果將我們自己的回撥函式傳遞給第三方外掛或者庫,就要考慮一些不可控因素

    • 呼叫回撥過早
    • 呼叫回撥過晚(或不被呼叫)
    • 呼叫回撥次數過多或者過少

promise的存在就是為了解決以上問題
雖然我們日常寫回撥函式不會有這麼嚴格的要求,但是如果不這樣去寫回撥函式,就會存在隱患,當在團隊協作的時候,顯得編碼規範顯得尤為重要

本文不重點介紹如何使用promise,重點介紹的是promise解決了哪些非同步回撥出現的問題。

什麼是promise

我們來看一個場景,有助於我們瞭解promise

設想一下這個場景,我去KFC,交給收銀員10元,下單買一個漢堡,下單付款。到這裡,我已經發出了一個請求(買漢堡),啟動了一次交易。
但是做漢堡需要時間,我不能馬上得到這個漢堡,收銀員給我一個收據來代替漢堡。到這裡,收據就是一個承諾(promise),保證我最後能得到漢堡。
所以我需要好好的保留的這個收據,對我來說,收據就是漢堡,雖然這張收據不能吃,我需要等待漢堡做好,等待收銀員叫號通知我
等待的過程中,我可以做些別的事情
收銀員終於叫到了我的號,我用收據換來了漢堡
當然還有一種情況,當我去櫃檯取漢堡的時候,收銀員告訴我漢堡賣光了,做漢堡的師傅受傷了等等原因,導致了我無法得到這個漢堡
雖然我有收據(承諾),但是可能得到漢堡(成功),可能得不到漢堡(失敗)
我由等待漢堡變成了等到或者等不到,這個過程不可逆,

上面很形象的介紹了promise,上面的等待漢堡和得到漢堡,漢堡賣光了,得不到漢堡,分別對應promise的三種狀態
三種狀態:pending(進行中)、fulfilled(已成功)和rejected(已失敗)(一旦狀態改變,就不會再變)

回撥函式呼叫過早

呼叫過早就是將非同步函式作為同步處理了,
我們之前說過,javascript以單執行緒同步的方式執行主執行緒,遇到非同步會將非同步函式放入到任務佇列中,
當主執行緒執行完畢,會迴圈執行任務佇列中的函式,也就是事件迴圈,直到任務佇列為空。

事件迴圈和任務佇列

事件迴圈就像是一個遊樂場,玩過一個遊戲後,你需要重新排到隊尾才能再玩一次
任務佇列就是,在你玩過一個遊戲後,可以插隊接著玩

我們看一個栗子

    const promise = new Promise((resolve, reject) => {
      resolve("成功啦")
    });
    promise.then(res => {
      console.log(res);
      console.log("我是非同步執行的");
    })
    console.log(`我在主執行緒`);

看下輸出,重點看輸出順序

//我在主執行緒
//成功啦
//我是非同步執行的

直接手動是promise的狀態切為成功狀態,console.log(“我是非同步執行的”);這段程式碼也是非同步執行的
提供給then()的回撥永遠都是非同步執行的,所以promise中不會出現回撥函式過早執行的情況

回撥函式呼叫過晚或不被呼叫

回撥函式呼叫過晚

回撥函式呼叫過晚的處理原理和呼叫過早很類似,
在promise的then()中存放著非同步函式,所有的非同步都存在於js的任務佇列中,當js的主執行緒執行完畢後,會依次執行任務佇列中的內容,不會出現執行過晚的情況

回撥函式不被呼叫

我們用栗子說話

    const promise = new Promise((resolve, reject) => resolve(`成功啦`))
    promise.then(s => console.log(s));
    console.log(`我在主執行緒`);

成功狀態的輸出

//我在主執行緒
//成功啦

成功狀態下回撥被呼叫
繼續看一下失敗的回撥

    const promise = new Promise((resolve, reject) => reject(`失敗啦`))
   promise.then(null, s => console.log(s));
   console.log(`我在主執行緒`);

失敗狀態的輸出

//我在主執行緒
//失敗啦

失敗狀態下回撥被呼叫
所以說,不管是失敗還是成功,回撥函式都會被呼叫

回撥函式呼叫次數過多或者過少

呼叫次數過多

我們之前說了promise有三種狀態
pending(進行中)、fulfilled(已成功)和rejected(已失敗)狀態一旦狀態改變,就不會再變
一個栗子

    const promise = new Promise((resolve, reject) => {
      reject(`失敗啦`)
      resolve(`成功啦`)
    });
    promise.then(res => {
      console.log(`我是非同步執行的成功:${res}`);
    },err=>{
      console.log(`我是非同步執行的失敗:${err}`);
    }).catch(err => {
      console.log(err);
    })
    console.log(`我在主執行緒`);

輸出

//我在主執行緒
//我是非同步執行的失敗:失敗啦

當狀態變為失敗時,就不會再變為成功,成功的函式也不會執行,反之亦然

呼叫次數過少

回撥函式正常是呼叫一次,過少=>0次=>回撥函式不被呼叫,上面剛剛討論過

原文連結

參考連結
JavaScript Promise 迷你書
Promise 物件
ES6 系列之我們來聊聊 Promise

相關文章