特皮技術團隊:一年經驗菜鳥前端眼中的異步編程

前端開發必不可少,什麼是異步編程

  • 因爲javascript語言是一門「單線程」的語言,因此,javascript就像一條流水線,僅僅是一條流水線而已,要麼加工,要麼包裝,不能同時進行多個任務和流程。javascript

  • 而做爲前端開發,在面試與工做中相信你們必定被問過或常常須要用到異步編程,那麼什麼是異步編程呢?css

  • 一首歌,一篇文章 歡迎你關注公衆號   html

首先咱們先區分一下什麼是同步編程,什麼是異步編程。

  • 同步編程:咱們都知道代碼的執行順序是自上而下執行的,那麼同步就是須要每個任務都完成之後再去執行下一個任務,執行順序與排列順序是一致的。壞處,只要有一個任務耗時很長,後面任務都必須排隊等着,常見的瀏覽器無響應,死循環。前端

  • 異步編程:每個任務有一個或多個回調函數,前一個任務執行完後,不是執行下一個任務,而是執行回調函數,後一個任務是不等前一個任務結束就執行的,因此程序的執行順序與任務的排列順序是不一致的。java

  • 簡單的說咱們能夠將異步編程理解爲在約定的時間內完成的操做。node

  • 舉個簡單的例子:es6

    • 假設你設置了一個次日 7:00 的鬧鐘,那麼咱們設置完,是一直在等待鬧鐘的提醒,再去作下一件事,仍是去作別的事情,相信你們都不會傻傻的在那等,在編程裏這就異步編程。

異步編程怎麼判斷:是否阻塞 ? 同步阻塞 ,異步不阻塞。

  • 那麼常見的異步編程有什麼呢?
  • setTimeout
  • Ajax
  • Promise
  • async函數
  • 接下來咱們經過代碼看看異步編程是如何執行的

定時器(setTimeOut)

  • 在規定的時間內完成操做:點擊按鈕,會打印「我先執行」  接着打印 「執行定時器」。能夠看到雖然時間設置爲0,可是定時器裏的任務並非先被執行
<body>
    <button>點擊觸發定時器</button>
    <script>
        let btn = document.querySelector('button');
        btn.onclick = function () {
            setTimeout(function () {
                alert('執行定時器');
            }, 0)

        }
        console.log('我先執行');
    </script>
</body>

Ajax (異步JavaScript 和 XML)

  • 首先介紹咱們先一下Ajax。
AJAX = Asynchronous JavaScript and XML(異步的 JavaScript 和 XML)
  • Ajax是一種無需從新加載整個頁面的狀況下,可以更新部分網頁的技術。web

  • 接着咱們經過一個簡單的例子來看看ajax的強大(爲了方便調用接口咱們直接使用網上連接https://cnodejs.org/api),爲了觀看效果明顯一些會使用點擊事件讓你們看看觸發結果面試

  • Ajax現代瀏覽器均支持XMLHTTPRequest對象,可是IE五、IE6須要兼容,下面就不作兼容處理了ajax

<body>
    <button onclick="loadXML()">點擊獲取結果</button>
    <div></div>
    <script>

        function loadXML() {
            let xmlhttp = new XMLHttpRequest();
            xmlhttp.onreadystatechange = function () {
                if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
                    console.log(xmlhttp)
                    document.querySelector('div').innerHTML = JSON.parse(xmlhttp.responseText).data[0].content;
                }
            }
            xmlhttp.open('GET''https://cnodejs.org/api/v1/topics'true)
            xmlhttp.send()
            console.log('我先執行了')
        }
    </script>

</body>

  • 能夠看獲得結果依然是先打印了後面的「我先執行了」

Promise對象

  • 什麼是promise對象 :

  • 從英文翻譯來說就是」承諾「,既然是承諾確定就須要去完成嘛,因此仍是對應了上面說的 ,異步編程能夠理解爲在約定的時間內完成的操做。

  • promise有三種狀態:pendding ,fulfilled,rejected

  • pendding:初始狀態,不成功,不失敗,
  • fulfilled:操做成功
  • rejected:操做失敗
  • 當promise狀態發生改變,就會觸發then()裏的響應函數處理後續步驟;當promise狀態一經改變,不會再變。

  • promise的使用

setTimeout(() => console.log(1), 0); // 異步
const p = new Promise((resolve, reject) => {
    console.log(2);                 // 同步
    setTimeout(() => {
        console.log(4)               // 異步優先級低於promise
    }, 0)
    resolve(3);
    console.log(5);                 // 同步
})
console.log(6)                      // 同步
p.then(value => {
    console.log(value);             // 異步
})

console.log(7)                      // 同步
  • 上面這一段代碼其實在面試中很常見
  • 執行結果是  2 5 6 7 3 1 4
  • 再講結果以前咱們應該瞭解一下es6的新增的任務隊列 是在事件循環之上的(onclick, setTimeout,Ajax)
  • onclick 是瀏覽器內核的 DOM Binding 模塊來處理的,當事件觸發的時候,回調函數會當即添加到任務隊列中。
  • setTimeout 是瀏覽器內核的 timer 模塊進行的延時處理,當時間到達後纔會回調添加到任務隊列中。
  • Ajax 是瀏覽器內核 network 模塊在網絡請求完成以後,將回調添加到任務隊列中。
  • 代碼也寫了,結果也看了,可是咱們爲何要用promise呢?

  • 常見的回答都是解決回調地獄,其實promise也是用於解決異步編程的,

  • 在promise未出現前,咱們的異步編程都是經過純回調解決的

  • 舉個例子

// 純回調
function createdAsync(value,success,catch){
    if (value){
        success()
    }else{
        catch()
    }
}
const success = function (){
    console.log('成功')
}
const catch = catch(){
    console.log('失敗')
}

createdAsync(value,success,catch)


// promise 
const promise = createdAsync(value);
setTimeout(()=>{
    promise.then(success,catch);
},1000);

  • 能夠看得出 純回調的形式是先指定回調函數,在咱們想要啓動異步任務前就必須指定好成功、失敗的回調函數,並且咱們不能在它完成後在指定回調函數,等他執行完畢已經獲取不到數據了;

  • 而 promise是經過執行一個函數,這個函數返回一個promise對象,異步操做是在這個Promise對象內部進行的,也就是Promise構造函數執行時當即調用executor 函數,此時異步任務開始了,可是並不須要指定成功、失敗的回調函數。

  • 能夠再在來看看前面的代碼(咱們將代碼寫的簡短一些)

const p = new Promise((resolve, reject) => {
    console.log("executor執行器函數"); //  executor執行器函數
    // 異步任務
    resolve("異步")
})
p.then(value => {
    console.log(value);
})
console.log("new Promise 以後"
  • 打印結果是:   」executor執行器函數「  、」new Promise 以後「  、「異步」
  • 因此promsie其實不僅是解決回調地獄問題,而說到了回調地獄,其實async函數顯得更加優化。

Async函數

  • 什麼是async函數

  • ES2017 標準引入了 async 函數,使得異步操做變得更加方便。

  • async 函數是什麼?一句話,它就是 Generator 函數的語法糖。

  • 可是我更加傾向於async函數是Promise語法糖。咱們經過下面的例子看看。

 // 以往定義promise的時候
  new Promise((resolve,reject)=>{
    console.log('開始');
    resolve('異步');
  }).then(value => {console.log(value)})
// async函數
  async function fn(){}
  console.log(fn())  // Promise{<fulfilled>:undefined}
  • 能夠看得出async 的返回值就是一個Promise對象 而且默認返回一個執行結果爲成功的Promise對象,也就是 new Promise() 的語法糖

  • 接着咱們看看 async 下的await

// promise
const promise = new Promise((resolve, reject) => {
    resolve('異步');
})

promise.then(value => {
    console.log(value)
})

// async
async function fn(callback) {
    const val = await callback;
    console.log(val)
}

fn(promise)
}
  • 能夠看得出await 就是then的語法糖

  • 接着咱們看看這個語法糖爲咱們解決了什麼問題

// promise
new Promise((resolve, reject) => {
    resolve(1);
}).then(value => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(value + 1)
        })
    });
}).then(value => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(value + 1)
        })
    });
}).then(value => {
    console.log(value + 1)
})
console.log('我先')

// ascyn 

async function fn() {
    const one = await new Promise((resolve, reject) => {
        resolve(1)
    })
    const two = await new Promise((resolve, reject) => {
        resolve(one + 1)
    })
    console.log(two);
    console.log('我先')
}

fn()
  • 能夠看得出 promise的執行會 先打印出 「我先」,而asyn函數 會將異步執行完畢再進行下面的操做,因此async函數不只實現了異步編程,而且在代碼的上來講執行順序與排列順序是一致的。

  • 文中說的內容偏入門級別,也是結合本身所學以及理解。

  • 最後想說的是隨着前端開發的不斷髮展,前端開發人員掌握的技術已經再也不是以前的html+css了(俗稱的切圖仔),而且前端開發人員須要掌握的技術不亞於後端了,甚至須要掌握一些後端知識

  • 本文使用mdnice排版

  • 往期推薦閱讀:

  • 從零手寫逐步實現Promise A+標準的全部方法

  • 乾貨!阿里P6手寫源碼面試題集錦



本文分享自微信公衆號 - 前端巔峯(Java-Script-)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索