小技巧:使用Array.reduce建立Promise回撥鏈

suti發表於2019-03-04

前端的同學們肯定會遇到多個非同步組合的場景,比如需要等待多個請求一同返回後處理或是發出多個請求只取第一個返回結果,這裡我們可以使用Promise強大的api: Promise.allPromise.race來處理。但當我們需要將多個非同步處理順序執行時,應該怎麼辦呢?

假設一個需求:我們需要傳送一堆請求,但是需要在每個請求返回後再傳送下一個:

    let urls = [
        `api.xxx.com/a`,
        `api.xxx.com/b`,
        `api.xxx.com/c`,
        ...
    ]
複製程式碼

常規做法我們會這樣:

    await Axios.get(urls[0])
    await Axios.get(urls[1])
    await Axios.get(urls[2])
    ...
複製程式碼

如果說請求陣列長度是已知的,這樣做也無可厚非。
但如果請求陣列長度是未知的呢,我們就要辦法讓js自己去構造Promise回撥鏈了。

先看一則文件:
Array.reduce

    reduce() 方法接收一個函式作為累加器(accumulator),陣列中的每個值(從左到右)開始縮減,最終為一個值。
    
    arr.reduce([callback, initialValue])
    引數
        callback
            執行陣列中每個值的函式,包含四個引數:
        previousValue
            上一次呼叫回撥函式返回的值,或者是提供的初始值(initialValue)
        currentValue
            陣列中當前被處理的元素
        currentIndex
            當前被處理元素在陣列中的索引, 即currentValue的索引.如果有initialValue初始值, 從0開始.如果沒有從1開始.
        array
            呼叫 reduce 的陣列
        initialValue
            可選引數, 作為第一次呼叫 callback 的第一個引數。
    返回值
        最後一次呼叫回撥函式返回的結果
複製程式碼

Array.reduce 可以處理陣列中的每一個元素,並將處理後的結果作為引數傳遞給下一個處理函式。這樣,我們可以在callback中構造一個Promise,並傳遞給下一個處理函式。

    urls.reduce(async (previousValue, currentValue) => {
        await previousValue
        return Axios.get(currentValue)
    }, Promise.resolve())
    
    // 不用async
    urls.reduce((previousValue, currentValue) => {
        return previousValue.then(() => Axios.get(currentValue))
    }, Promise.resolve())
複製程式碼

這樣我們就利用 Array.reduce 建立了一個Promise回撥鏈,類似於:

    Promise.then(() => new Promise()).then(() => new Promise()).then(() => ...)
複製程式碼

當然了,這裡的處理函式只用Axios做了個例子,任何一個返回Promise的方法都可以。類似的,我們可以用這個做很多東西,比如業務處理、動畫載入。更多奇思妙想正等著你。

相關文章