異步發展史,此次必定會!

啥叫異步?啥叫同步?🙉

同步:

每次只有一個線程其餘線程被阻塞git

簡單來講,就是任務一個接着一個完成。只要一個沒完成其餘被堵塞github

異步:

不用阻塞當前線程來等待處理完成,而是容許後續操做,直至其它線程將處理完成,並回調通知此線程。編程

簡單來講,就是任務同時進行,中途可能須要等待其餘任務完成才能進行當前任務。json

js爲啥須要異步?🙉

js是單線程的,若是有一個任務咱們要花很長時間,那豈不是這以後的代碼全被阻塞了。redux

Js的異步編程發展史👏

其實異步編程的發展的目的是愉快地coding:用同步寫法寫異步api

1. callback回調函數:

回調函數:異步操做執行完後觸發執行的函數,相似以下格式:promise

$.get("http://xxx.xxxx.com/api",callback);
複製代碼

優勢:

  • 簡單
  • 符合傳統js

缺點:

  • 容易造成回調地獄 若是:咱們須要請求A接口獲得學生id,經過學生id請求B接口獲得學生成績,再通學生成績請求獲得排名(不是很好的例子 但大概這個意思)
fetch({
    url: "/student",
    data:1,
    success: function (id) {
        fetch({
            url: "/score",
            data:id,
            success: function (score) {
                fetch({
                    url: "/rank",
                    data:score,
                    success: function (rank) {
                        console.log(rank)
                    }
                })
            }
        })
    }
})
複製代碼
  • 回調嵌套會致使代碼難以維護
  • 而且不方便統一處理錯誤(不能 try catch)

2. Promise:

Promise 必定程度上解決了回調地獄的問題,Promise 最先由社區提出和實現,ES6 將其寫進了語言標準,統一了用法,原生提供了Promise對象。Promise對象簡單說就是一個容器,裏面保存着某個將來纔會結束的事件(一般是一個異步操做)的結果。從語法上說,Promise 是一個對象,從它能夠獲取異步操做的消息。Promise 提供統一的 API,各類異步操做均可以用一樣的方法進行處理。bash

fetch(1).then(id=> {
    return fetch(id);
}).then(score => {
    return fetch(score);
}).then(rank=> {
    console.log(rank)
}).catch(reason => {
    console.log(reason);
});
複製代碼

優勢:

  • 一旦狀態改變,就不會再變,任什麼時候候均可以獲得這個結果
  • 能夠將異步操做以同步操做的流程表達出來,避免了層層嵌套的回調函數

缺點:

  • 沒法取消 Promise
  • 當處於pending狀態時,沒法得知目前進展到哪個階段
  • 錯誤不能被 try catch

3. Generator

Generator 函數是 ES6 提供的一種異步編程解決方案,整個 Generator 函數就是一個封裝的異步任務,或者說是異步任務的容器。異步操做須要暫停的地方,都用 yield 語句註明。 將函數分割出好多個部分,調用一次next就會繼續向下執行。返回結果是一個迭代器,迭代器有一個next方法。多線程

function my_co (it) {
    return new Promise((resolve, reject) => {
        function next(data) {
            let {value, done} = it.next(data);
            if(!done) {
                value.then(val => {
                    next(val);
                }, reject);
            }else{
                resolve(value);
            }
            
        }
        next();
    });
}

function *getData() {
    let res=yield fetch('/student',()=>{});
    let res1=yield fetch('/score',res1,()=>{});
    let res2=yield fetch('/rank',res2,()=>{});
    return res2
}
let it=getData()
let id=it.next(it)
let score=it.next(id)
let rank=it.next(score)
my_co(getData()).then(data => {
    console.log(data);
});
複製代碼

特別注意:
看上去彷佛咱們平常業務中generator的異步寫法不經常使用,但實際上在某些庫的開發中它有着重要用途。
對!說的就是那個redux-saga
並且仔細想一想還有那個異步編程的方式有這麼大的控制性,想停就停!app

4. Aync await

async-await實際上是一個語法糖,它的實現就是將 Generator函數和自動執行器(co),包裝在一個函數中。
async至關於用Promise.resolve()包裹其函數返回值。
await至關於generatorpromise的語法糖,至關於yield,但他以外的同步代碼能夠自動執行
🔔若是沒有依賴性的話也能夠用promise.all

//如上面那個例子
function fetchData(api) {
      let result
      let url = 'http://localhost:3000'
      fetch(url + api, {
        method: 'GET',
        headers: {
          'Content-Type': 'application/json',
        },
      })
        .then((res) => {
          return res.json()
        })
        .then((json) => {
          console.log(json)
          result=json
        })
       return result
}
async function getData(){
    let id=await fetchData('/student')
    let score=await fetchData('/score')
    let rank=await fetchData('/rank')
}
複製代碼

優勢:

  • 代碼清晰,
  • 不用像 Promise 寫不少 then ,
  • 能夠處理回調地獄的問題
  • 而且錯誤能夠被try catch
async function main() {
  try {
    const val1 = await firstStep();
    const val2 = await secondStep(val1);
    const val3 = await thirdStep(val1, val2);

    console.log('Final: ', val3);
  }
  catch (err) {
    console.error(err);
  }
}
複製代碼

🌰試一試:

git 地址
內含關於四種方式異步編程的例子,歡迎你們試一試👀

附加

  1. js是怎麼實現異步執行的?
    執行棧:
    執行棧能夠看做是一個存儲函數調用的棧結構,遵循先進後出。 執行js時就會往執行棧放入函數。遇到異步則被掛起放入任務隊列。
    EventLoop:
    等執行棧空了,eventloop就會把要執行的代碼從隊列中放入執行棧。\
  2. WebWorker是真的多線程嗎?詳見【WebWorker,此次必定會!】
相關文章
相關標籤/搜索