欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

JS異步編程Promise對象詳解

 更新時間:2022年06月26日 12:04:03   作者:小旭2021  
本文詳細(xì)講解了JS異步編程之Promise對象,文中通過示例代碼介紹的非常詳細(xì)。對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下

1、單線程模型

單線程模型指的是,JavaScript 只在一個線程上運(yùn)行。也就是說,JavaScript 同時只能執(zhí)行一個任務(wù),其他任務(wù)都必須在后面排隊等待。注意,JavaScript 只在一個線程上運(yùn)行,不代表 JavaScript 引擎只有一個線程。事實(shí)上,JavaScript 引擎有多個線程,單個腳本只能在一個線程上運(yùn)行(稱為主線程),其他線程都是在后臺配合。

JavaScript 之所以采用單線程,而不是多線程,跟歷史有關(guān)系。JavaScript 從誕生起就是單線程,原因是不想讓瀏覽器變得太復(fù)雜,因?yàn)槎嗑€程需要共享資源、且有可能修改彼此的運(yùn)行結(jié)果,對于一種網(wǎng)頁腳本語言來說,這就太復(fù)雜了。

如果 JavaScript 同時有兩個線程,一個線程在網(wǎng)頁 DOM 節(jié)點(diǎn)上添加內(nèi)容,另一個線程刪除了這個節(jié)點(diǎn),這時瀏覽器應(yīng)該以哪個線程為準(zhǔn)?是不是還要有鎖機(jī)制?
所以,為了避免復(fù)雜性,JavaScript 一開始就是單線程,這已經(jīng)成了這門語言的核心特征,將來也不會改變。

2、同步任務(wù)和異步任務(wù)

程序里面所有的任務(wù),可以分成兩類:同步任務(wù)(synchronous)和異步任務(wù)(asynchronous)。

同步任務(wù)是那些沒有被引擎掛起、在主線程上排隊執(zhí)行的任務(wù)。只有前一個任務(wù)執(zhí)行完畢,才能執(zhí)行后一個任務(wù)。

異步任務(wù)是那些被引擎放在一邊,不進(jìn)入主線程、而進(jìn)入任務(wù)隊列的任務(wù)。只有引擎認(rèn)為某個異步任務(wù)可以執(zhí)行了(比如 Ajax 操作從服務(wù)器得到了結(jié)果),該任務(wù)(采用回調(diào)函數(shù)的形式)才會進(jìn)入主線程執(zhí)行。排在異步任務(wù)后面的代碼,不用等待異步任務(wù)結(jié)束會馬上運(yùn)行,也就是說,異步任務(wù)不具有“堵塞”效應(yīng)。

舉例來說,Ajax 操作可以當(dāng)作同步任務(wù)處理,也可以當(dāng)作異步任務(wù)處理,由開發(fā)者決定。如果是同步任務(wù),主線程就等著 Ajax 操作返回結(jié)果,再往下執(zhí)行;如果是異步任務(wù),主線程在發(fā)出 Ajax 請求以后,就直接往下執(zhí)行,等到 Ajax 操作有了結(jié)果,主線程再執(zhí)行對應(yīng)的回調(diào)函數(shù)。

3、任務(wù)隊列和事件循環(huán)

JavaScript 運(yùn)行時,除了一個正在運(yùn)行的主線程,引擎還提供一個任務(wù)隊列(task queue),里面是各種需要當(dāng)前程序處理的異步任務(wù)。(實(shí)際上,根據(jù)異步任務(wù)的類型,存在多個任務(wù)隊列。為了方便理解,這里假設(shè)只存在一個隊列。)首先,主線程會去執(zhí)行所有的同步任務(wù)。等到同步任務(wù)全部執(zhí)行完,就會去看任務(wù)隊列里面的異步任務(wù)。

如果滿足條件,那么異步任務(wù)就重新進(jìn)入主線程開始執(zhí)行,這時它就變成同步任務(wù)了。等到執(zhí)行完,下一個異步任務(wù)再進(jìn)入主線程開始執(zhí)行。一旦任務(wù)隊列清空,程序就結(jié)束執(zhí)行。異步任務(wù)的寫法通常是回調(diào)函數(shù)。一旦異步任務(wù)重新進(jìn)入主線程,就會執(zhí)行對應(yīng)的回調(diào)函數(shù)。

如果一個異步任務(wù)沒有回調(diào)函數(shù),就不會進(jìn)入任務(wù)隊列,也就是說,不會重新進(jìn)入主線程,因?yàn)闆]有用回調(diào)函數(shù)指定下一步的操作。JavaScript 引擎怎么知道異步任務(wù)有沒有結(jié)果,能不能進(jìn)入主線程呢?答案就是引擎在不停地檢查,一遍又一遍,只要同步任務(wù)執(zhí)行完了,引擎就會去檢查那些掛起來的異步任務(wù),是不是可以進(jìn)入主線程了。這種循環(huán)檢查的機(jī)制,就叫做事件循環(huán)(Event Loop)。

維基百科的定義是:“事件循環(huán)是一個程序結(jié)構(gòu),用于等待和發(fā)送消息和事件(a programming construct that waits for and dispatches events or messages in a program)”。

4、異步操作的模式

4.1回調(diào)函數(shù)

f2寫成f1的回調(diào)函數(shù)。

function f1(callback) {
  // ...
  callback();
}
 
function f2() {
  // ...
}
 
f1(f2);

回調(diào)函數(shù)的優(yōu)點(diǎn)是簡單、容易理解和實(shí)現(xiàn),缺點(diǎn)是不利于代碼的閱讀和維護(hù),各個部分之間高度耦合(coupling),使得程序結(jié)構(gòu)混亂、流程難以追蹤(尤其是多個回調(diào)函數(shù)嵌套的情況),而且每個任務(wù)只能指定一個回調(diào)函數(shù)。

4.2事件監(jiān)聽

f1.on('done', f2);
 
function f1() {
  setTimeout(function () {
    // ...
    f1.trigger('done');
  }, 1000);
}

f1.trigger('done')表示,執(zhí)行完成后,立即觸發(fā)done事件,從而開始執(zhí)行f2

這種方法的優(yōu)點(diǎn)是比較容易理解,可以綁定多個事件,每個事件可以指定多個回調(diào)函數(shù),而且可以“去耦合”(decoupling),有利于實(shí)現(xiàn)模塊化。缺點(diǎn)是整個程序都要變成事件驅(qū)動型,運(yùn)行流程會變得很不清晰。閱讀代碼的時候,很難看出主流程。

4.3 發(fā)布/訂閱

事件完全可以理解成“信號”,如果存在一個“信號中心”,某個任務(wù)執(zhí)行完成,就向信號中心“發(fā)布”(publish)一個信號,其他任務(wù)可以向信號中心“訂閱”(subscribe)這個信號,從而知道什么時候自己可以開始執(zhí)行。這就叫做“發(fā)布/訂閱模式”(publish-subscribe pattern),又稱“觀察者模式”(observer pattern)。

f2向信號中心jQuery訂閱done信號。

jQuery.subscribe('done', f2);
 
function f1() {
  setTimeout(function () {
    // ...
    jQuery.publish('done');
  }, 1000);
}

上面代碼中,jQuery.publish('done')的意思是,f1執(zhí)行完成后,向信號中心jQuery發(fā)布done信號,從而引發(fā)f2的執(zhí)行。

f2完成執(zhí)行后,可以取消訂閱(unsubscribe)。

jQuery.unsubscribe('done', f2);

這種方法的性質(zhì)與“事件監(jiān)聽”類似,但是明顯優(yōu)于后者。因?yàn)榭梢酝ㄟ^查看“消息中心”,了解存在多少信號、每個信號有多少訂閱者,從而監(jiān)控程序的運(yùn)行。

5、Promise 對象的狀態(tài)

Promise 對象通過自身的狀態(tài),來控制異步操作。Promise 實(shí)例具有三種狀態(tài)。

  • 異步操作未完成(pending)
  • 異步操作成功(fulfilled)
  • 異步操作失?。╮ejected)

上面三種狀態(tài)里面,fulfilledrejected合在一起稱為resolved(已定型)。

這三種的狀態(tài)的變化途徑只有兩種。

  • 從“未完成”到“成功”
  • 從“未完成”到“失敗”

一旦狀態(tài)發(fā)生變化,就凝固了,不會再有新的狀態(tài)變化。這也是 Promise 這個名字的由來,它的英語意思是“承諾”,一旦承諾成效,就不得再改變了。這也意味著,Promise 實(shí)例的狀態(tài)變化只可能發(fā)生一次。

因此,Promise 的最終結(jié)果只有兩種。

  • 異步操作成功,Promise 實(shí)例傳回一個值(value),狀態(tài)變?yōu)閒ulfilled。
  • 異步操作失敗,Promise 實(shí)例拋出一個錯誤(error),狀態(tài)變?yōu)閞ejected。

6、Promise 構(gòu)造函數(shù)

JavaScript 提供原生的Promise構(gòu)造函數(shù),用來生成 Promise 實(shí)例。

var promise = new Promise(function (resolve, reject) {
  // ...
 
  if (/* 異步操作成功 */){
    resolve(value);
  } else { /* 異步操作失敗 */
    reject(new Error());
  }
});

上面代碼中,Promise構(gòu)造函數(shù)接受一個函數(shù)作為參數(shù),該函數(shù)的兩個參數(shù)分別是resolvereject。它們是兩個函數(shù),由 JavaScript 引擎提供,不用自己實(shí)現(xiàn)。

resolve函數(shù)的作用是,將Promise實(shí)例的狀態(tài)從“未完成”變?yōu)?ldquo;成功”(即從pending變?yōu)?code>fulfilled),在異步操作成功時調(diào)用,并將異步操作的結(jié)果,作為參數(shù)傳遞出去。reject函數(shù)的作用是,將Promise實(shí)例的狀態(tài)從“未完成”變?yōu)?ldquo;失敗”(即從pending變?yōu)?code>rejected),在異步操作失敗時調(diào)用,并將異步操作報出的錯誤,作為參數(shù)傳遞出去。

下面是一個例子。

function timeout(ms) {
  return new Promise((resolve, reject) => {
    setTimeout(resolve, ms, 'done');
  });
}
 
timeout(100)

上面代碼中,timeout(100)返回一個 Promise 實(shí)例。100毫秒以后,該實(shí)例的狀態(tài)會變?yōu)?code>fulfilled。

7、then() 用法辨析

Promise 的用法,簡單說就是一句話:使用then方法添加回調(diào)函數(shù)。但是,不同的寫法有一些細(xì)微的差別,請看下面四種寫法,它們的差別在哪里?

// 寫法一
f1().then(function () {
  return f2();
});
 
// 寫法二
f1().then(function () {
  f2();
});
 
// 寫法三
f1().then(f2());
 
// 寫法四
f1().then(f2);

為了便于講解,下面這四種寫法都再用then方法接一個回調(diào)函數(shù)f3。寫法一的f3回調(diào)函數(shù)的參數(shù),是f2函數(shù)的運(yùn)行結(jié)果。

f1().then(function () {
  return f2();
}).then(f3);

寫法二的f3回調(diào)函數(shù)的參數(shù)是undefined。

f1().then(function () {
  f2();
  return;
}).then(f3);

寫法三的f3回調(diào)函數(shù)的參數(shù),是f2函數(shù)返回的函數(shù)的運(yùn)行結(jié)果。

f1().then(f2())
  .then(f3);

寫法四與寫法一只有一個差別,那就是f2會接收到f1()返回的結(jié)果。

f1().then(f2)
  .then(f3);

8、Promise 優(yōu)缺點(diǎn)

優(yōu)點(diǎn):讓回調(diào)函數(shù)變成了規(guī)范的鏈?zhǔn)綄懛?,程序流程可以看得很清楚。它有一整套接口,可以?shí)現(xiàn)許多強(qiáng)大的功能,比如同時執(zhí)行多個異步操作,等到它們的狀態(tài)都改變以后,再執(zhí)行一個回調(diào)函數(shù);再比如,為多個回調(diào)函數(shù)中拋出的錯誤,統(tǒng)一指定處理方法等等。

而且,Promise 還有一個傳統(tǒng)寫法沒有的好處:它的狀態(tài)一旦改變,無論何時查詢,都能得到這個狀態(tài)。這意味著,無論何時為 Promise 實(shí)例添加回調(diào)函數(shù),該函數(shù)都能正確執(zhí)行。所以,你不用擔(dān)心是否錯過了某個事件或信號。如果是傳統(tǒng)寫法,通過監(jiān)聽事件來執(zhí)行回調(diào)函數(shù),一旦錯過了事件,再添加回調(diào)函數(shù)是不會執(zhí)行的。

缺點(diǎn):編寫的難度比傳統(tǒng)寫法高,而且閱讀代碼也不是一眼可以看懂。你只會看到一堆then,必須自己在then的回調(diào)函數(shù)里面理清邏輯。

9、微任務(wù)

Promise 的回調(diào)函數(shù)屬于異步任務(wù),會在同步任務(wù)之后執(zhí)行。

new Promise(function (resolve, reject) {
  resolve(1);
}).then(console.log);
 
console.log(2);
// 2
// 1

上面代碼會先輸出2,再輸出1。因?yàn)?code>console.log(2)是同步任務(wù),而then的回調(diào)函數(shù)屬于異步任務(wù),一定晚于同步任務(wù)執(zhí)行。

但是,Promise 的回調(diào)函數(shù)不是正常的異步任務(wù),而是微任務(wù)(microtask)。它們的區(qū)別在于,正常任務(wù)追加到下一輪事件循環(huán),微任務(wù)追加到本輪事件循環(huán)。這意味著,微任務(wù)的執(zhí)行時間一定早于正常任務(wù)。

setTimeout(function() {
  console.log(1);
}, 0);
 
new Promise(function (resolve, reject) {
  resolve(2);
}).then(console.log);
 
console.log(3);
// 3
// 2
// 1

上面代碼的輸出結(jié)果是321。這說明then的回調(diào)函數(shù)的執(zhí)行時間,早于setTimeout(fn, 0)。因?yàn)?code>then是本輪事件循環(huán)執(zhí)行,setTimeout(fn, 0)在下一輪事件循環(huán)開始時執(zhí)行。

到此這篇關(guān)于Promise異步編程模式的文章就介紹到這了。希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • javascript 動態(tài)改變層的Z-INDEX的代碼style.zIndex

    javascript 動態(tài)改變層的Z-INDEX的代碼style.zIndex

    javascript 動態(tài)改變層的Z-INDEX的代碼style.zIndex...
    2007-08-08
  • 瀏覽器兼容console對象的簡要解決方案分享

    瀏覽器兼容console對象的簡要解決方案分享

    不同瀏覽器或者版本之間對于console對象的支持不盡相同,而console方法在開發(fā)調(diào)試過程中都是不錯的工具。難道要在上線前把所有console.xxxx去掉以保證某些瀏覽器不報錯么。其實(shí)可以變通解決
    2013-10-10
  • javascript實(shí)現(xiàn)英文首字母大寫

    javascript實(shí)現(xiàn)英文首字母大寫

    本文給大家總結(jié)了幾種可以實(shí)現(xiàn)英文首字母大寫的javascript腳本,另附上一個CSS的實(shí)現(xiàn)方法,非常的簡單實(shí)用,這里推薦給大家,有需要的小伙伴可以參考下。
    2015-04-04
  • 抓取JavaScript動態(tài)加載的內(nèi)容的方法總結(jié)

    抓取JavaScript動態(tài)加載的內(nèi)容的方法總結(jié)

    JavaScript動態(tài)加載的內(nèi)容常見于現(xiàn)代Web應(yīng)用中,用于增強(qiáng)用戶體驗(yàn)和減少初始頁面加載時間,然而,這些動態(tài)加載的內(nèi)容對于傳統(tǒng)的網(wǎng)頁抓取工具來說往往是不可見的,本文主要介紹了有JavaScript動態(tài)加載的內(nèi)容如何抓取,需要的朋友可以參考下
    2024-09-09
  • JS跨域請求的問題解析

    JS跨域請求的問題解析

    在本篇內(nèi)容里小編給大家整理了關(guān)于解決JS跨域請求的問題知識點(diǎn),需要的朋友們參考學(xué)習(xí)下。
    2018-12-12
  • BootStrap中的Fontawesome 圖標(biāo)

    BootStrap中的Fontawesome 圖標(biāo)

    這篇文章主要介紹了BootStrap中Fontawesome 圖標(biāo)的相關(guān)知識,需要的朋友可以參考下
    2017-05-05
  • 詳解小程序緩存插件(mrc)

    詳解小程序緩存插件(mrc)

    這篇文章主要介紹了詳解小程序緩存插件(mrc),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-08-08
  • JavaScript本地儲存:localStorage、sessionStorage、cookie的使用

    JavaScript本地儲存:localStorage、sessionStorage、cookie的使用

    這篇文章主要介紹了JavaScript本地儲存:localStorage、sessionStorage、cookie的使用,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-10-10
  • JavaScript簡單實(shí)現(xiàn)的仿微博留言功能示例

    JavaScript簡單實(shí)現(xiàn)的仿微博留言功能示例

    這篇文章主要介紹了JavaScript簡單實(shí)現(xiàn)的仿微博留言功能,涉及javascript頁面元素屬性動態(tài)操作相關(guān)實(shí)現(xiàn)技巧,需要的朋友可以參考下
    2019-01-01
  • js判斷客戶端是iOS還是Android等移動終端的方法

    js判斷客戶端是iOS還是Android等移動終端的方法

    本文為大家介紹下使用js判斷客戶端是iOS還是Android等移動終端,示例代碼如下,感興趣的朋友可以參考下
    2013-12-12

最新評論