摘要:為什么要異步編程我們在寫前端代碼時,經(jīng)常會對做事件處理操作,比如點擊激活焦點失去焦點等再比如我們用請求數(shù)據(jù),使用回調(diào)函數(shù)獲取返回值。這些都屬于異步編程。回調(diào)有多個狀態(tài),當(dāng)響應(yīng)成功和失敗都有不同的回調(diào)函數(shù)。
為什么要異步編程
我們在寫前端代碼時,經(jīng)常會對dom做事件處理操作,比如點擊、激活焦點、失去焦點等;再比如我們用ajax請求數(shù)據(jù),使用回調(diào)函數(shù)獲取返回值。這些都屬于異步編程。
也許你已經(jīng)大概知道JavaScript引擎單線程的概念,那么這種單線程模式和異步編程有什么關(guān)系呢?
JavaScript引擎中,只有一個主線程,當(dāng)執(zhí)行JavaScript代碼塊時,不允許其他代碼塊執(zhí)行,而事件機制和回調(diào)機制的代碼塊會被添加到任務(wù)隊列(或者叫做堆棧)中,當(dāng)符合某個觸發(fā)回調(diào)或者事件的時候,就會執(zhí)行該事件或者回調(diào)函數(shù)。
上面這段話的意思可以這樣理解,假設(shè)你是一個修仙者,你去闖一個秘境,這個秘境就是主線程,你只能一直深入下去,直到找到寶物和出口,而你還有一個自身的儲物空間,這個空間就類似堆棧,你在儲物空間放了很多你可能用到的法寶或者丹藥,這些東西就是回調(diào)函數(shù)和事件函數(shù),當(dāng)你遇到危險或者滿足某個條件時,就可以從儲物空間拿出你當(dāng)前需要的東西。
好吧,不扯這么遠(yuǎn),下面看正題。
事件模型:
瀏覽器初次渲染DOM的時候,我們會給一些DOM綁定事件函數(shù),只有當(dāng)觸發(fā)了這些DOM事件函數(shù),才會執(zhí)行他們。
const btn = document.querySelector(".button") btn.onclick = function(event) { console.log(event) }
回調(diào)模式:
nodejs中可能非常常見這種回調(diào)模式,但是對于前端來說,ajax的回調(diào)是最熟悉不過了。ajax回調(diào)有多個狀態(tài),當(dāng)響應(yīng)成功和失敗都有不同的回調(diào)函數(shù)。
$.post("/router", function(data) { console.log(data) })
回調(diào)也可能帶來一個問題,那就是地獄回調(diào),不過幸運的是,我從進(jìn)入前端界開始,就使用react,跳過了很多坑,特別是地獄回調(diào),一直沒有機會在工作中遇見到,真是遺憾。
Promise事件函數(shù)沒有問題,我們用的很爽,問題出在回調(diào)函數(shù),尤其是指地獄回調(diào),Promise的出現(xiàn)正是為了避免地獄回調(diào)帶來的困擾。
推薦你看JavaScript MDN Promise教程,然后再結(jié)合本文看,你就能學(xué)會使用Promise了。
Promise是什么Promise的中文意思是承諾,也就是說,JavaScript對你許下一個承諾,會在未來某個時刻兌現(xiàn)承諾。
Promise生命周期react有生命周期,vue也有生命周期,就連Promise也有生命周期,現(xiàn)在生命周期咋這么流行了。
Promise的生命周期:進(jìn)行中(pending),已經(jīng)完成(fulfilled),拒絕(rejected)
Promise被稱作異步結(jié)果的占位符,它不能直接返回異步函數(shù)的執(zhí)行結(jié)果,需要使用then(),當(dāng)獲取異常回調(diào)的時候,使用catch()。
這次我們使用axios插件的代碼做例子。axios是前端比較熱門的http請求插件之一。
1、創(chuàng)建axios實例instance。
import axios from "axios" export const instance = axios.create()
2、使用axios實例 + Promise獲取返回值。
const promise = instance.get("url") promise.then(result => console.log(result)).catch(err => console.log(err))使用Promise構(gòu)建函數(shù)創(chuàng)建新的Promise
Promise構(gòu)造函數(shù)只有一個參數(shù),該參數(shù)是一個函數(shù),被稱作執(zhí)行器,執(zhí)行器有2個參數(shù),分別是resolve()和reject(),一個表示成功的回調(diào),一個表示失敗的回調(diào)。
new Promise(function(resolve, reject) { setTimeout(() => resolve(5), 0) }).then(v => console.log(v)) // 5
記住,Promise實例只能通過resolve或者reject函數(shù)來返回,并且使用then()或者catch()獲取,不能在new Promise里面直接return,這樣是獲取不到Promise返回值的。
1、我們也可以使用Promise直接resolve(value)。
Promise.resolve(5).then(v => console.log(v)) // 5
2、也可以使用reject(value)
Promise.reject(5).catch(v => console.log(v)) // 5
3、執(zhí)行器錯誤通過catch捕捉。
new Promise(function(resolve, reject) { if(true) { throw new Error("error!!") } }).catch(v => console.log(v.message)) // error!!全局的Promise拒絕處理
不重要的內(nèi)容,不用細(xì)看。
這里涉及到nodejs環(huán)境和瀏覽器環(huán)境的全局,主要說的是如果執(zhí)行了Promise.reject(),瀏覽器或者node環(huán)境并不會強制報錯,只有在你調(diào)用catch的時候,才能知道Promise被拒絕了。
這種行為就像是,你寫了一個函數(shù),函數(shù)內(nèi)部有true和false兩種狀態(tài),而我們希望false的時候拋出錯誤,但是在Promise中,并不能直接拋出錯誤,無論Promise是成功還是拒絕狀態(tài),你獲取Promise生命周期的方法只能通過then()和catch()。
nodejs環(huán)境:
node環(huán)境下有個對象叫做process,即使你沒寫過后端node,如果寫過前端node服務(wù)器,也應(yīng)該知道可以使用process.ENV_NODE獲取環(huán)境變量。為了監(jiān)聽Promise的reject(拒絕)情況,NodeJS提供了一個process.on(),類似jQuery的on方法,事件綁定函數(shù)。
process.on()有2個事件
unhandledRjection:在一個事件循環(huán)中,當(dāng)Promise執(zhí)行reject(),并且沒有提供catch()時被調(diào)用。
正常情況下,你可以使用catch捕捉reject。
Promise.reject("It was my wrong!").catch(v => console.log(v))
但是,有時候你不總是記得使用catch。你就需要使用process.on()
let rejected rejected = Promise.reject("It was my wrong!") process.on("unhandledRjection", function(reason, promise) { console.log(reason.message) // It was my wrong! console.log(rejected === promise) // true })
rejectionHandled:在一個事件循環(huán)后,當(dāng)Promise執(zhí)行reject,并且沒有提供catch()時被調(diào)用。
let rejected rejected = Promise.reject(new Error("It was my wrong!")) process.on("rejectionHandled", function(promise) { console.log(rejected === promise) // true })
異同:
事件循環(huán)中、事件循環(huán)后,你可能很難理解這2個的區(qū)別,但是這不重要,重要的是,如果你通過了catch()方法來捕捉reject操作,那么,這2個事件就不會生效。
瀏覽器環(huán)境:
和node環(huán)境一樣,都提供了unhandledRjection、rejectionHandled事件,不同的是瀏覽器環(huán)境是通過window對象來定義事件函數(shù)。
let rejected rejected = Promise.reject(new Error("It was my wrong!")) window.rejectionHandled = function(event) { console.log(event) // true } rejectionHandled()
將代碼在瀏覽器控制臺執(zhí)行一遍,你就會發(fā)現(xiàn)報錯了:Uncaught (in promise) Error: It was my wrong!
耶,你成功了!報錯內(nèi)容正是你寫的reject()方法里面的錯誤提示。
Promise鏈?zhǔn)秸{(diào)用這個例子中,使用了3個then,第一個then返回 s * s,第二個then捕獲到上一個then的返回值,最后一個then直接輸出end。這就叫鏈?zhǔn)秸{(diào)用,很好理解的。我只使用了then(),實際開發(fā)中,你還應(yīng)該加上catch()。
new Promise(function(resolve, reject) {
try {
resolve(5)
} catch (error) {
reject("It was my wrong!!!")
}
}).then(s => s * s).then(s2 => console.log(s2)).then(() => console.log("end"))
// 25 "end"
在Promise的構(gòu)造函數(shù)中,除了reject()和resolve()之外,還有2個方法,Promise.all()、Promise.race()。
Promise.all():
前面我們的例子都是只有一個Promise,現(xiàn)在我們使用all()方法包裝多個Promise實例。
語法很簡單:參數(shù)只有一個,可迭代對象,可以是數(shù)組,或者Symbol類型等。
Promise.all(iterable).then().catch()
示例:傳入3個Promise實例
Promise.all([ new Promise(function(resolve, reject) { resolve(1) }), new Promise(function(resolve, reject) { resolve(2) }), new Promise(function(resolve, reject) { resolve(3) }) ]).then(arr => { console.log(arr) // [1, 2, 3] })
Promise.race():語法和all()一樣,但是返回值有所不同,race根據(jù)傳入的多個Promise實例,只要有一個實例resolve或者reject,就只返回該結(jié)果,其他實例不再執(zhí)行。
還是使用上面的例子,只是我給每個resolve加了一個定時器,最終結(jié)果返回的是3,因為第三個Promise最快執(zhí)行。
Promise.race([ new Promise(function(resolve, reject) { setTimeout(() => resolve(1), 1000) }), new Promise(function(resolve, reject) { setTimeout(() => resolve(2), 100) }), new Promise(function(resolve, reject) { setTimeout(() => resolve(3), 10) }) ]).then(value => { console.log(value) // 3 })Promise派生
派生的意思是定義一個新的Promise對象,繼承Promise方法和屬性。
class MyPromise extends Promise { //重新封裝then() success(resolve, reject) { return this.then(resolve, reject) } //重新封裝catch() failer(reject) { return this.catch(reject) } }
接著我們來使用一下這個派生類。
new MyPromise(function(resolve, reject) { resolve(10) }).success(v => console.log(v)) // 10
如果只是派生出來和then、catch一樣的方法,我想,你不會干這么無聊的事情。
Promise和異步的聯(lián)系Promise本身不是異步的,只有他的then()或者catch()方法才是異步,也可以說Promise的返回值是異步的。通常Promise被使用在node,或者是前端的ajax請求、前端DOM渲染順序等地方。
比Promise更牛逼的異步方案在本章你只需要了解有async這個未來的方案,推薦不會的趕緊去網(wǎng)上找資料學(xué),反正我是已經(jīng)在實際項目中全面開展async了。
async function a() { await function() {}} }總結(jié)
Promise是什么、怎么用、怎么獲取返回值?是本章的中心內(nèi)容,多看幾遍,你會發(fā)現(xiàn)使用Promise是非常簡單的事情。
=> 返回文章目錄
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/84553.html
摘要:為什么要異步編程我們在寫前端代碼時,經(jīng)常會對做事件處理操作,比如點擊激活焦點失去焦點等再比如我們用請求數(shù)據(jù),使用回調(diào)函數(shù)獲取返回值。這些都屬于異步編程?;卣{(diào)有多個狀態(tài),當(dāng)響應(yīng)成功和失敗都有不同的回調(diào)函數(shù)。 為什么要異步編程 我們在寫前端代碼時,經(jīng)常會對dom做事件處理操作,比如點擊、激活焦點、失去焦點等;再比如我們用ajax請求數(shù)據(jù),使用回調(diào)函數(shù)獲取返回值。這些都屬于異步編程。 也許你...
摘要:回調(diào)函數(shù)模式類似于事件模型,因為異步代碼也會在后面的一個時間點才執(zhí)行如果回調(diào)過多,會陷入回調(diào)地獄基礎(chǔ)可以當(dāng)做是一個占位符,表示異步操作的執(zhí)行結(jié)果。函數(shù)可以返回一個,而不必訂閱一個事件或者向函數(shù)傳遞一個回調(diào)函數(shù)。 主要知識點:Promise生命周期、Promise基本操作、Promise鏈、響應(yīng)多個Promise以及集成PromiseshowImg(https://segmentfaul...
摘要:從最開始的到封裝后的都在試圖解決異步編程過程中的問題。為了讓編程更美好,我們就需要引入來降低異步編程的復(fù)雜性。異步編程入門的全稱是前端經(jīng)典面試題從輸入到頁面加載發(fā)生了什么這是一篇開發(fā)的科普類文章,涉及到優(yōu)化等多個方面。 TypeScript 入門教程 從 JavaScript 程序員的角度總結(jié)思考,循序漸進(jìn)的理解 TypeScript。 網(wǎng)絡(luò)基礎(chǔ)知識之 HTTP 協(xié)議 詳細(xì)介紹 HTT...
摘要:的翻譯文檔由的維護很多人說,阮老師已經(jīng)有一本關(guān)于的書了入門,覺得看看這本書就足夠了。前端的異步解決方案之和異步編程模式在前端開發(fā)過程中,顯得越來越重要。為了讓編程更美好,我們就需要引入來降低異步編程的復(fù)雜性。 JavaScript Promise 迷你書(中文版) 超詳細(xì)介紹promise的gitbook,看完再不會promise...... 本書的目的是以目前還在制定中的ECMASc...
閱讀 1852·2021-08-19 11:12
閱讀 1426·2021-07-25 21:37
閱讀 990·2019-08-30 14:07
閱讀 1268·2019-08-30 13:12
閱讀 653·2019-08-30 11:00
閱讀 3530·2019-08-29 16:28
閱讀 994·2019-08-29 15:33
閱讀 2969·2019-08-26 13:40