javascript - 異步操做

javascript與其餘語言的經典不一樣在於,javascript是異步的,而其餘語言是同步的。這裏,咱們介紹一下javascript中異步的幾種方式。javascript

幾種異步方式

  • 回調函數
  • promise
  • generator
  • async / await

回調函數

回調函數,是早期js中普遍使用的一種回調方式,jquery中的ajax方法就是經典的回調函數模式。java

回調函數的寫法中,回調是放在函數參數裏面的,執行的過程看起來沒有同步寫法那麼明瞭。jquery

在回調次數多的狀況下,可能會形成回調地獄,就是嵌套太深。因此es後面提出的異步方案也是不斷對異步作優化。git

$.ajax({
    url: 'http://binnie.com/getdata',
    type: 'GET',
    data: {},
    success: {
        // success todo
    },
    error: {
        // error todo
    }
})
複製代碼

promise

promise,是es6中提供的一種解決方案,相比於回調函數,promise的寫法更加同步化一些~es6

狀態

promise的狀態分爲三種:github

  • pending
  • resolved
  • rejected

狀態的變化只有兩個過程,而且狀態一旦發生改變,就永遠不可逆。ajax

  • pending -> resolved
  • pending -> rejected

基礎

Promise是一個構造函數,咱們可使用new來進行實例化的操做。數組

咱們來看一個簡單的🌰promise

咱們能夠看到new一個Promise對象的時候,傳入的是一個函數,該函數攜帶兩個參數:resolve&reject,分別爲成功操做&失敗操做,函數內部的操做是同步執行的,因此當執行代碼的時候,立刻就能夠打印出1異步

new返回的對象是一個Promise對象,Promise對象有幾個內置的函數,這幾個函數是在Promise的同步函數執行完以後可能觸發的函數。

每次執行完其中一個函數,是能夠接着鏈式執行的,由於每次執行完一次,都會返回一個新的Promise對象。

  • then
  • finally
  • catch
let promise = new Promise((resolve, reject) => {
    // 這裏是同步執行的
    console.log(1)
    // 異步操做
    return resolve('binnie')
});
promise.then((resolve) => {
    // 成功回調,即執行resolve()
    console.log('then')
},(reject) => {
    // 失敗回調,即執行reject()
    console.log('err')
}).finally(() => {
    // 完成回調,不管成功/失敗都會到的回調
    console.log('finally')
}).catch((err) => {
    // 錯誤回調,前面任何一步拋出的錯誤
    console.log(err)
});

// 1
// then
複製代碼

實例方法

實例方法是new Promise返回的對象的原型方法。好比then方法 -> Promise.prototype.then。

then

then方法接受兩個參數,第一個爲resolve回調,第二個爲reject回調

promise.then((resolve) => {
    // resolve參數爲promise resolve的時候的參數
    // 當promise執行resolve時,會進入該回調
},(reject) => {
    // reject參數爲promise reject的時候的參數
    // 當promise拋出異常 或者 執行reject時,會進入該回調
})
複製代碼

finally

finally回調函數不接受任何參數,只作finally處理,相似請求的complete操做。

promise.finally(() => {
    // resolve or reject 以後的操做
})
複製代碼

catch

catch方法,用於捕獲異常。

在promise中的異常能夠在兩個位置進行捕獲,then方法的第二個函數 or catch方法。通常狀況下,咱們會在catch中捕獲。

在promise中,錯誤也是會冒泡的,若是then操做沒有添加reject方法,那麼錯誤就會在後面的catch捕獲,不過,若是沒有對獲取進行捕獲,錯誤其實會被promise本身吃掉,而不會暴露在最外面。

promise.then(() => {
    console.log('then')
    throw new Error()
}).catch((err) => {
    console.log(err)
})
複製代碼

Promise方法

Promise方法,直接調用便可,返回的也是promise對象。

Promise.all(array)

Promise.all,接受一個數組作爲參數,參數中的對象會被轉化爲promise對象

當數組中的全部promise對象resolve時,all的promise的狀態才變爲resolve

當數組中的任一promise對象reject時,all的promise的狀態就變爲reject

Promise.all 相似咱們日常使用的 &&

Promise.all([promise,2,3]).then((resolve) => {
    // 參數也是一個數組,爲all數組對應的resolve參數
    // ['binnie',2,3]
    console.log(resolve)
}).catch((err) => {
    // 錯誤爲第一個reject時拋出的錯誤
    console.log(err)
})
複製代碼

Promise.race(array)

Promise.race跟all的用法相似,也是接受一個數組作爲參數

數組中最早改變狀態的,那個狀態就會同步給race的狀態

Promise.race([1,promise,3]).then((resolve) => {
    // 參數爲第一個狀態變爲resolve的參數
    console.log(resolve)
}).catch((err) => {
    // 參數爲第一個狀態變爲reject的參數
    console.log(err)
})
複製代碼

Promise.resolve()

返回一個狀態爲resolved的promise對象。

Promise.resolve()可攜帶參數,參數會作爲返回對象的參數。

Promise.reject()

返回一個狀態爲rejected的promise對象。

Promise.reject()可攜帶參數,參數會作爲返回對象的參數。

關於promise,也可查看樓主以前的文章 ES6之promise基礎篇

generator

generator能夠理解爲一個暫停執行函數。每次執行next()來往下走,直到結束,狀態中止。

generator函數的寫法,就是在函數後面加一個星號 function* ,在函數內部,可使用 yield 來進行暫停的操做。

function* foo() {
    console.log(0)
    yield 1
    yield 2
    yield 3
    return 4
    yield 5
}
// 調用generator函數的時候,函數並不會被執行
let f = foo()
console.log(f)         
// 調用next時函數纔會開始執行,當遇到yield時暫停
// 下一次next時會從上次暫停的位置繼續執行
// 當遇到return時 or 函數執行完成時,函數結束
console.log(f.next())   
console.log(f.next())
console.log(f.next())
console.log(f.next())
console.log(f.next())
複製代碼

async / await

generator相對來講可讀性會比較差,因而乎有來async函數,表示函數內部會有異步操做,async函數至關因而generator函數的語法糖。

下面咱們來看下async函數的寫法

// 定義一個async函數,代表函數內部要執行異步操做
async function fun() {
    // await後面跟着異步操做
    // 函數會等待await操做完成以後再繼續往下走
    let obj = await request();
    return obj;
}
// async函數執行完以後會返回一個promise對象,函數返回值可再then中取到
let ret = fun()
ret.then((resolve) => {
    console.log(resolve)
})
複製代碼

當async函數中遇到await時,就會進行等待,而後再執行下一步的操做,若是不必串行,咱們能夠改爲並行的寫法,這樣能夠節省函數執行時間。

// 串行寫法
async function fun() {
    let obj1 = await request1();
    let obj2 = await request2();
    return;
}

// 並行寫法
async function fun() {
    let obj1 = request1();
    let obj2 = request2();
    let ret1 = await obj1;
    let ret2 = await obj2;
    return;
}

// Promise.all寫法
async function fun() {
    let [obj1, obj2] = await Promise.all([request1(), request2()]) ;
    return;
}
複製代碼

寫在最後

異步操做的核心都是同樣的,只是寫法不一樣而已。

具體要採用哪一種異步方法,仍是要看咱們實際的項目須要。

相關文章
相關標籤/搜索