Javascript如何理解全國甲卷高考作文

前言

2022年全國甲卷高考語文作文已然出爐,筆者看到後居然想起瞭這個表情包

好傢夥,還好我提前成為瞭一名默默在卷大學生,不然以這篇作文的難度足以讓我招架不住。話不多說,咱們來看看百度作文給出的題目解析:

這是一則材料作文。材料由兩個部分組成,第一部分是現象,介紹傳統文化經典《紅樓夢》中眾人給匾額題名三種不同的方式;第二部分是對上述三種題名方法的評價,暗示瞭對傳統文化的繼承、發展與創新。“直接移用”,“借鑒化用”,“根據情境獨創”,也就是直接學習、借鑒、創新三步走。在此基礎上,由文學作品中的情節內涵拓展到更廣泛的領域,引發學生深入思考。

透過這個解析我們可以看到這則材料作文由兩個部分構成,第一部分是現象,具體是指《紅樓夢》中眾人給匾額題名三種不同的方式;第二部分則是對上述三種題名方法的評價。由於筆者水平有限,所以決定在直接引用、借鑒化用、根據情境獨創這三個題眼上下手,那麼它們所對應的順序也自然是學習、借鑒、創新,通過一步一步的摸索,最終達到好的理想狀況,而又經過對題目解析的反復揣摩,筆者腦中忽然靈光一現,我能不能以JavaScript中處理並發請求的幾種方式為素材來試著寫寫這篇作文呢?

因筆者水平有限,本篇文章所理解內容僅為個人觀點。我會嘗試將“翼然”、“瀉玉”、“沁芳”分為三個階段,每個階段都是對前一個階段的升華,通過一步一步的摸索、前進,最終達到“沁芳”的理想層面。

我之前的介紹過關於異步任務的題目雖然也不少,但究竟應該如何優雅的處理並發請求呢?要知道在ES6之前,我們發送依賴請求大概如下所示:

場景:根據用戶id(001),獲取到用戶姓名後,再獲取用戶性別,再獲取用戶年齡

function getUserName(id, callback) {
    setTimeout(function () {
        callback('001姓名為鯊魚辣椒')
    }, 1000)
}
function getUserSex(id, callback) {
    setTimeout(function () {
        callback('001性別為男')
    }, 1000)
}
function getUserAge(id, callback) {
    setTimeout(function () {
        callback('001年齡為5')
    }, 1000)
}
// 開始套娃
getUserName('001', function (name) {
    console.log(name)
    getUserSex('001', function (sex) {
        console.log(sex)
        getUserAge('001', function (age) {
            console.log(age)
        })
    })
})

由上述代碼可知,這種寫法通常會飽受回調地獄的“折磨”,尤其是當依賴的請求變的越來越多時,我們的“屎山”也便一發不可收拾瞭。當Promise問世之後,我們來看看又該如何優化,而這一優化是不是也代表著“翼然”向“瀉玉”所作出的轉變呢?

function getUserName(id) {
    return new Promise(function (resolve) {
        setTimeout(function () {
            resolve('001姓名為鯊魚辣椒')
        }, 1000)
    })
}
function getUserSex(id) {
    return new Promise(function (resolve) {
        setTimeout(function () {
            resolve('001性別為男')
        }, 1000)
    })
}
function getUserAge(id) {
    return new Promise(function (resolve) {
        setTimeout(function () {
            resolve('001年齡為5')
        }, 1000)
    })
}
// 禁止套娃,一切都開始扁平瞭起來
getUserName('001').then(function (data) {
    console.log(data)
    return getUserSex('001')
}).then(function (data) {
    console.log(data)
    return getUserAge('001')
}).then(function (data) {
    console.log(data)
})

由此可見,當應用瞭Promise之後,這座“屎山”也隨之被鏟平瞭。如果說then的鏈式調用足以磨平請求代碼的“屎山”,那麼我們今天用一個需求,來展現async降維打擊的能力之強,下面我們開始逐漸處理並發請求,也一道解開“翼然”、“瀉玉”、“沁芳”之間的關系

回顧

Promise

Promise的特點是同步執行代碼,且在未決議之前擁有三種狀態:初始化狀態為pending、成功狀態為resolve、失敗狀態為rejected。在未調用resolve以及rejected之前,狀態始終保持為pending,調用resolverejected後狀態分別為成功/失敗,要註意的是狀態一旦確定,不可更改。每個Promise對象都會返回一個then方法,這個then方法會在Promise對象成功/失敗時如期調用,因為這是Promise所作出的承諾,例如:

var p = new Promise((resolve, rejected) => {
    // 1秒鐘之後調用resolve,將初始化狀態改為成功狀態
    // 註意,在此1秒鐘之前,p的狀態始終為初始化狀態,因為Promise尚未作出決議
    setTimeout(resolve, 1000)
})
// then方法會在約1秒鐘後執行
p.then(data => console.log(data))

Promise.all

all方法接收一個具有iterator接口的數據,例如數組。要註意的是這個數組中的每一個成員都必須是一個Promise對象,all方法會返回這些Promise對象成功或失敗的值,例如:

var p = async () =>  await new Promise(r => setTimeout(r, 1000))
var f = Promise.all([p, p])
// then方法會在約1秒鐘後執行
p().then(data => console.log(data))

async

async函數始終會返回一個Promise對象,這個Promise對象的狀態取決於async函數的返回值。await關鍵字的職責就是異步求值,通常後面會是一個Promise對象,await關鍵字會“自動”幫我們then這個Promise對象,然後拿到數據進行返回。註意,隻有當await後面的這個Promise對象決議瞭,async函數才會繼續往下執行,否則交出線程轉而執行其它任務,例如:

// f會在約1秒鐘後決議
var f = async () =>  await new Promise(r => setTimeout(r, 1000))
// then方法會在約1秒鐘後執行
f().then(data => console.log(data))

需求

回顧完成,接下來我們通過這個需求來解開“翼然”、“瀉玉”、“沁芳”之間的關系。比如我有一個裝有一百個書本id的數組,我需要獲取每個書本的詳細內容,雖然是一百本書,但由於隻有一個接口,所以需要重復調用,隻不過在調用時每次的入參id均不同而已

實現

準備準備

數據

books中存儲著每個書本的id,我們需要通過這些枯燥無味的id來拿到每個書本所對應的信息

var books = [
    {id: "0000"},
    {id: "1111"},
    {id: "2222"},
    {id: "3333"},
    // more...
]

封裝請求

我們先封裝一個request方法,用於請求單個書本id所對應的信息。因為沒有這麼合適的接口來供我們練習,所以此處暫時決定使用setTimeout來代替請求區域

// request方法會在約1秒鐘後“請求成功”
var request = id => new Promise(resolve => setTimeout(resolve, 1000))

原生

通過xhr封裝一個請求,要註意的是這裡的請求函數requestJs並未用到Promise對象,而是使用瞭原汁原味的回調模式,因為在ES6之前Promise尚未面世,這也正對應瞭最開始的階段——“翼然”

var info = []
var requestJs = (id, callback) => {
    var xhr = new XMLHttpRequest()
    open('GET', 'url')
    xhr.send(null)
    xhr.onreadyStatechange = function () {
        if (xhr.readyState == 4) {
            if (xhr.status >= 200 && xhr.status < 300) {
                callback(xhr.responseText)
            }
        }
    }
}
// 通過定時器來模擬請求
var requestJsPolyill = (id, callback) => {
    setTimeout(() => {
        callback(id)
    }, 1000)
}
var judge = data => {
    info.push(data)
    if (books.length === info.length) {
        console.log('請求到的數據:', info)
    }
}
books.forEach(v => requestJsPolyill(v.id, judge))

Promise

每請求到一個書本的信息就追加到info數組中,每次追加後,來判斷booksinfo的長度是否相同,如果相同,則代表所有書本信息均已請求完畢,如果不同則代表尚有書本信息未請求成功,我們正是通過這種方式來處理並發請求,可以看到的是Promise這種方式正是對原生JS的一種化用,或者說是一種更好的改變,所以也就符合瞭“瀉玉”這一階段

// 存儲獲取到的書本信息
var info = []
var f = new Promise(resolve => {
    books.forEach(v => {
        // 根據books中每個id進行請求
        request(v.id).then(() => {
            // 每請求到一個書本信息都會放在info中
            info.push(v.id)
            // 當請求完的信息數量與原始書本的數量對應起來時,則代表全部請求完成
            if (books.length === info.length) {
                resolve(info)
            }
        })
    })
})
// 獲取完全部書本信息後執行then方法
f.then(data => {
    console.log('請求到的數據:', data)
})

async

由於map方法會返回一個新數組,所以此處通過map來對books中的每一個書本id都加工成一個請求,通過request來完成這個請求,而request又會返回一個Promise對象,這樣一來,返回的這個新數組中的每個成員就都是一個Promise對象,而Promise.all方法在接收到這個新數組後,會並發執行所有Promise對象,等到所有Promise對象都決議後,then方法才會被調用。使用async來完成這個需求,無論是代碼簡潔程度還是可讀性,都大大的超越瞭“翼然”和“瀉玉”這兩個階段,也可以說這正是“沁芳”的獨特所在

// 當新數組中的所有Promise全部決議後,then會被如期調用
var f = Promise.all(books.map(async v => await request(v.id)))
f.then(data => {
    console.log('請求到的數據:', data)
})

文末

我們分別使用瞭三種方式來處理並發請求,期間我們也闡述瞭它們各自的特點,而這也正好對應瞭文章開頭所提到的材料作文兩個部分:眾人題匾的三種方式以及對三種方式的評價。在Promise未面世之前,我們通常使用原生JavaScript來處理請求,常用的手法也自然是使用回調地獄,這一階段算是處於初始階段,也就正好對應瞭“翼然”這一層面,而通過對原生JavaScript請求方式的改善,將多層嵌套逐漸扁平化來優化代碼,通過這一點Promise也正符合瞭“瀉玉”這一階段,看到async的實現方式才明白原來代碼還可以如此優雅,比如可讀性、擴展性都要比前者強出不少,可能這大概就是創新的魅力所在瞭吧。無論是“翼然”還是“瀉玉”,距離我們理想的代碼層面尚有些許不足,而正是這些不足的存在,我們才能一步一步的創新、前進,最終達到“沁芳”的理想層面

到此這篇關於Javascript如何理解全國甲卷高考作文的文章就介紹到這瞭,更多相關js理解全國甲卷高考作文內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: