首頁 >web前端 >js教程 >弄清楚promise、async、await

弄清楚promise、async、await

hzc
hzc轉載
2020-07-04 09:50:021905瀏覽

上一篇呢,主要是聊了聊同步、異步,他們各自引申出來的'執行棧'、'消息隊列',以及'宏任務'、'微任務',如果大家對這幾個概念不太了解,可以去這個連結:

https://www.jianshu.com/p/61e7844e68d8

宏任務與微任務都是非同步的,其中包括ajax請求、計時器等等,我們初步的了解promise,知道他是解決異步的一種方式,那麼我們常用的一共有哪幾種方法呢?

因為涉及的知識點比較多,這篇文章主要是講一下,回呼函數和promise:

一、回呼函數

先上代码:
function f2() {
    console.log('2222')
}
function f1(callback){
    console.log('111')
  setTimeout(function () {
    callback(); 
  }, 5000);
  console.log('3333')
}
f1(f2);

先看下打印值是:
111
3333
五秒后2222

相當於主執行緒執行完了,會透過回呼函數去呼叫f2函數,這個沒什麼毛病。但看下下面的例子:

现在我们读取一个文件,fileReader就是一个异步请求

// 这个异步请求就是通过回调函数的方式获取的

var reader = new FileReader()
var file = input.files[0]
reader.readAsText(file, 'utf-8',function(err, data){
    if(err){
        console.log(err)
    } else {
        console.log(data)
    }
})

現在看起來也很不錯,但是如果檔案上傳出錯了,我們還要在回調裡面做判斷,要是我們讀取完這個檔案接著要讀取多個文件呢?是不是應該這麼寫:

读取完文件1之后再接着读取文件2、3

var reader = new FileReader()
var file = input.files[0]
reader.readAsText(file1, 'utf-8',function(err1, data1){
    if(err1){
        console.log(err1)
    } else {
        console.log(data1)
    }
    reader.readAsText(file2, 'utf-8',function(err2, data2){
        if(err2){
            console.log(err2)
        } else {
            console.log(data2)
        }
        reader.readAsText(file3, 'utf-8',function(err3, data3){
            if(err3){
                console.log(err3)
            } else {
                console.log(data3)
            }
        })
    })
})

這麼寫可以實現需求,但是這個程式碼的可讀性就比較差,看起來就不那麼優雅,也就是我們常說的‘回調地獄’。那麼要怎麼破解這種嵌套式的回呼呢? ES6為我們提供了promise:

二、promise

首先我們從字面意思上理解一下什麼是promise? promise可以翻譯成承諾、保證,這個地方你可以理解為:

女朋友讓我做了一件事,雖然還沒幹完,但是我保證這件事會有一個結果給你,成功(fulfiled)或失敗(rejected),還有一個等待狀態(pending)。

还是先上例子

let promise = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve(2000) // 成功以后这个resolve会把成功的结果捕捉到
        // reject(2000) // 失败以后这个reject会把失败的结果捕捉到
    }, 1000)
    console.log(1111)
})

promise.then(res => {
    console.log(res) // then里面第一个参数就能拿到捕捉到的成功结果
}, err =>{
    console.log(res)// then里面第二个参数就能拿到捕捉到的失败结果
})

打印结果:

1111
2000(一秒以后)

1、then鍊式操作

Promise物件的then方法傳回一個新的Promise對象,因此可以透過鍊式呼叫then方法。

then方法接收兩個函數作為參數,第一個參數是Promise執行成功時的回調,第二個參數是Promise執行失敗時的回調,這個上面的例子說的很明白了,第二個參數捕捉的就是失敗的回呼。

兩個函數只會有一個被調用,這句話怎麼理解呢?
女朋友讓你去做番茄雞蛋湯,你要嘛就去做,要嘛就不做,叫外賣,肯定沒有第三種選擇。

函數的回傳值將被用作建立then回傳的Promise物件。這句話該怎麼理解呢?還是上例子:

let promise = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve(2000)
    }, 1000)
    console.log(1111)
})
promise.then(res => {
    console.log(res) // 这个地方会打印捕捉到的2000
    return res + 1000 // 这个函数的返回值,返回的就是这个promise对象捕捉到的成功的值
}).then(res => {
    console.log(res) //这个地方打印的就是上一个promise对象return的值
})

所以打印顺序应该是:

1111
2000
3000

剛才我們看到了then接受兩個參數,一個是成功的回調、一個是失敗的回調,看起來好像也不是那麼優雅,promise裡除了then還提供了catch方法:

2、catch捕捉操作

這個catch就是專門捕捉錯誤的回呼的,還是先看例子:

let promise = new Promise((resolve, reject) => {
    setTimeout(() => {
        reject(2000) // 失败以后这个reject会把失败的结果捕捉到
    }, 1000)
    console.log(1111)
})
promise.catch(res => {
    console.log(res) // catch里面就能拿到捕捉到的失败结果
})

打印结果:

1111
2000(一秒以后)

除了then和catch之外,promise還有兩個文法,all和race,我們也簡單用一下:

3、all

現在我們有這麼一個需求,一共有三個介面A、B、C,必須三個介面都成功以後,才能發起第四個請求,要怎麼實現?

鍊式呼叫

let getInfoA = new Promise((resolve, reject) => {
    console.log('小A开始执行了')
    resolve()
}).then(res => {
    let getInfoB = new Promise((resolve, reject) => {
        console.log('小B开始执行了')
        resolve()
    }).then(res => {
        let getInfoC = new Promise((resolve, reject) => {
            console.log('小C开始执行了')
            resolve()
        }).then(res => {
            console.log('全都执行完了!')
        })
    })
})

一層套一層的,好像不是那麼優雅

all

let getInfoA = new Promise((resolve, reject) => {
    console.log('小A开始执行了')
    resolve()
})
let getInfoB = new Promise((resolve, reject) => {
    console.log('小B开始执行了')
    resolve()
})
let getInfoC = new Promise((resolve, reject) => {
    console.log('小C开始执行了')
    resolve()
})
Promise.all([getInfoA, getInfoB, getInfoC]).then(res => {
   console.log('全都执行完了!')
})

接收一個Promise物件組成的陣列作為參數,當這個陣列所有的Promise物件狀態都變成resolved或是rejected的時候,它才會去呼叫then方法。非常完美,非常優雅。

4、race

現在又有一個需求,同樣是介面A、B、C,只要有一個回應了,我就可以調接口D,那怎麼實作呢?

1、傳統方式

let getInfoA = new Promise((resolve, reject) => {
    console.log('小A开始执行了')
    setTimeout((err => {
        resolve('小A最快')
    }),1000)
}).then(res =>{
    console.log(res)
})
let getInfoB = new Promise((resolve, reject) => {
    console.log('小B开始执行了')
    setTimeout((err => {
        resolve('小B最快')
    }),1001)
}).then(res =>{
    console.log(res)
})
let getInfoC = new Promise((resolve, reject) => {
    console.log('小C开始执行了')
    setTimeout((err => {
        resolve('小C最快')
    }),1002)
}).then(res =>{
    console.log(res)
})

打印结果

小A开始执行了
小B开始执行了
小C开始执行了
小A最快

這個方法得寫三遍,好像也不是那麼優雅,一起來看下race該怎麼寫?

2、race

let getInfoA = new Promise((resolve, reject) => {
    console.log('小A开始执行了')
    setTimeout((err => {
        resolve('小A最快')
    }),1000)
})
let getInfoB = new Promise((resolve, reject) => {
    console.log('小B开始执行了')
    setTimeout((err => {
        resolve('小B最快')
    }),1001)
})
let getInfoC = new Promise((resolve, reject) => {
    console.log('小C开始执行了')
    setTimeout((err => {
        resolve('小C最快')
    }),1002)
})
Promise.race([getInfoA, getInfoB, getInfoC]).then(res => {
    console.log(res)
})

打印结果

小A开始执行了
小B开始执行了
小C开始执行了
小A最快

與Promise.all相似的是,Promise.race都是以一個Promise物件組成的陣列作為參數,不同的是,只要當陣列中的其中一個Promsie狀態變成resolved或rejected時,就可以呼叫.then方法了。

promise是ES6用來解決非同步的一個方法,現在用的已經比較廣泛了,像我們常用的axios,他就是用promise封裝的,用起來非常方便。

除了promise,ES6也為我們提供了終極大招async、await,因為這兩個知識塊比較大,所以我們準備下一篇文章講。

個人的微信公眾號:小Jerry有話說,平常會發一些技術文章和讀書筆記,歡迎交流。

推薦教學:《JS教學

以上是弄清楚promise、async、await的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:jianshu.com。如有侵權,請聯絡admin@php.cn刪除