1. 程式人生 > 其它 >JavaScript 中的非同步程式設計

JavaScript 中的非同步程式設計

一、回撥函式

為了實現JS中的非同步操作,主要的解決方案就是利用回撥函式的方法

回撥函式是先呼叫,後定義的方式使用的。

如:

function d1(interval,callback){
    setTimeout(()=>{
        callback('hello world')
    },interval)
}

d1(1000,data=>{
    console.log(data)
})

二、Promise

Promise是ES6封裝的物件,Promise物件可以化解回撥地獄callback hell的情況。

Promise物件代表一個非同步操作,有三種狀態:pending

(進行中)、fulfilled(已成功)和rejected(已失敗)。只有非同步操作的結果,可以決定當前是哪一種狀態,任何其他操作都無法改變這個狀態。

一般用resolve來表示fulfilled

1.建構函式

new Promise((resolve,reject)=>{
    非同步方法
})

注意:

  • 建構函式中的函式引數會立即開始執行。也就是說,構造Promise時,非同步方法就會開始執行。
  • resolve,reject是兩個回撥方法

2.then

最重要的API(then)。then是當建構函式中非同步方法執行完畢後,才會執行的方法。then中的第一個引數就是建構函式中resolve的回撥,第二個引數就是建構函式中的reject的回撥:

then((data)=>{
    
},(err)=>{
    
})

3.then的鏈式呼叫

then可以鏈式呼叫,並且可以解決回撥地域的問題。如果then中resolve的回撥中有retuen Promise1物件,則當前then執行後返回一個Promise1物件。如:

封裝一個fs模組下的readFileAPI:

let pFileR=(FilePath)=>{
    return  new Promise((resolve,reject)=>{
    fs.readFile(FilePath,'utf8',(err,data)=>{
        if
(err){ reject(err) } resolve(data) }) }) } pFileR('./a.txt') .then((data)=>{ console.log(data) return pFileR('./b.txt') }) .then((data)=>{ console.log(data) return pFileR('./c.txt') }) .then((data)=>{ console.log(data) })

三、async函式

3.1 async函式使用環境

  • async函式是為了實現對非同步操作的控制而產生的。本身和Generator函式沒有區別,實際上就是後者的語法糖。async 函式配合await來代替Gennerator函式的*yield。如果不瞭解Gennerator函式的話沒有關係。直接從更好用的async開始學習。
  • 以前對於非同步操作的繼發執行(比如:非同步A執行完之後在執行非同步B)都是使用函式巢狀的方式,後來上文中介紹的Promise物件,現在有了ES7中正式提出的async函式來控制非同步函式。

3.2 async函式使用方式

  • function前加入async宣告該方法為async函式

  • 在函式體中,在非同步操作前加入await用以宣告,一般該非同步操作應當為Promise物件化的

  • 在一個await聲明後的非同步操作時,整個async函式將會暫停向下執行,直到當前非同步操作執行完畢後才會向下執行,但是當該非同步操作出錯時,async函式將會結束整個函式,並返回一個reject(err)Promise物件。如果要實現,當異步出錯時,也繼續向下執行async函式的話,需要配合try..catch.catch()使用。

  • async返回一個預設的promise物件,如果return一個promise物件的話則返回該Promise物件,如果return一個其他資料的的話則返回一個Promise物件,且該物件的resolve的引數為當時return的資料

3.3 async函式實現非同步繼發執行

function sleep(interval) {
    return new Promise(resolve => {
      setTimeout(resolve, interval);
    })
  }
  
  // 用法
  async function one2FiveInAsync() {
    for(let i = 1; i <= 5; i++) {
        await sleep(1000);  //實際上就是暫停one2FiveInAsync函式1s後再繼續執行
        console.log(i); 
    }
  }
  
  one2FiveInAsync()

/*
    result:
    after 1s : 1
    ....
    after 5s : 5
*/

3.4 實現異步出錯處理

3.4.1 使用try-catch捕獲異常

在下面這個例子中,放在同一個try中的當遇到一個await定義的非同步後將被catch捕獲異常,然後跳出try,繼續向下執行,當遇到沒有在try中的await定義的異步出錯時,退出了整個async函式

//try..catch方式
async function a(){
    try{
        await Promise.reject('出錯啦1')
        await Promise.reject('出錯啦2')
    }catch(e){
        console.log(e)
    }
    await Promise.reject('出錯啦3')
    await Promise.resolve('成功啦')
}

a().then(d=>{console.log(d)})
.catch(e=>{console.log(e)})

/*
    result:
        出錯啦1
        出錯啦3
*/

3.4.2 使用catch方法捕獲異常

async function a(){
    await Promise.reject('出錯啦!1').catch(e=>{console.log(e)})
    await Promise.reject('出錯啦!2').catch(e=>{console.log(e)})
    await Promise.resolve('成功啦')
}
a()
.then(d=>{console.log(d)})
.catch(e=>{console.log(e)})

/*
    result:
        出錯啦!1
        出錯啦!2
        undefined
*/

3.5 實現非同步的類併發執行

類併發執行,即非繼發執行。在async中實現類併發,可以配合Promise.all方法使用

 await Promise.all([Example1(),Example2()])