JS的非同步

来源:https://www.cnblogs.com/gzhjj/archive/2018/05/17/9044222.html
-Advertisement-
Play Games

1.非同步 程式中現在運行的部分和將來運行的部分之間的關係是非同步編程的核心。 多數JavaScript開發者從來沒有認真思考過自己程式中的非同步到底是如何出現的,以及為什麼會出現,也沒有探索過處理非同步的其他方法。一直以來,低調的回調函數就算足夠好的方法了。目前為止,還有很多人堅持認為回調函數完全夠用。 ...


1.非同步

程式中現在運行的部分和將來運行的部分之間的關係是非同步編程的核心。
多數JavaScript開發者從來沒有認真思考過自己程式中的非同步到底是如何出現的,以及為什麼會出現,也沒有探索過處理非同步的其他方法。一直以來,低調的回調函數就算足夠好的方法了。目前為止,還有很多人堅持認為回調函數完全夠用。
但是,作為在瀏覽器、伺服器以及其他能夠想到的任何設備上運行的一流編程語言,JavaScript面臨的需求日益擴大。為了滿足這些需求,JavaScript的規模和複雜性也在持續增長,對非同步的管理也越來越令人痛苦,這一切都迫切需要更強大、更合理的非同步方法。

1.1 分塊的程式

現在我們發出一個非同步Ajax請求,然後在將來才能得到返回的結果(通過使用回調函數)。

//ajax(...)是某個庫中提供的某個Ajax函數。
ajax("http://some.url.1",function myCallbackFunction(data){
    console.log(data);//得到一些數據
});
function now(){
    return 21;
}

function later(){
    answer = answer * 2;
    console.log("Meaning of life: ", answer);
}

var answer = now();

setTimeout(later, 1000);//Meaning of life: 42

setTimeout(...)設置了一個事件(定時)在將來執行,所以函數later()的內容會在之後的某個時間(從現在起1000毫秒之後)執行。
任何時候,只要把一段代碼包裝成一個函數,並指定它在響應某個事件(定時器、滑鼠點擊、Ajax響應等)時執行,你就是在代碼中創建了一個將來執行的塊,也由此在這個程式中引入了非同步機制。

1.2 事件迴圈

現在我們來澄清一件事情(可能令人震驚):儘管你顯然能夠編寫非同步JavaScript代碼,但直到最近(ES6),JavaScript才真正內建有直接的非同步概念。

JavaScript引擎並不是獨立運行的,它運行在宿主環境中,對多數開發者來說通常就是Web瀏覽器。經過最近幾年的發展,JavaScript已經超過了瀏覽器的範圍,進入了其他環境,比如通過像Node.js這樣的工具進入伺服器領域。實際上,JavaScript現如今已經嵌入到了從機器人到電燈泡等各種各樣的設備中。
所有這些環境都提供了一種機制來處理程式中多個塊的執行,且執行每個塊時調用JavaScript引擎,這種機制被稱為事件迴圈

ES6中Promise對事件迴圈隊列的調度運行能夠直接進行精細控制。

1.3 並行

非同步是關於現在和將來的時間間隙,而並行是關於能夠同時發生的事情。

var a = 20;

function foo(){
    a = a + 1;
}

function bar(){
    a = a * 2;
}

ajax("...",foo);
ajax("...",bar);

由於JavaScript的單線程特性,foo()bar()中的代碼具有原子性。也就是說,一旦foo()開始運行,它的所有代碼都會在bar()中的任意代碼運行之前完成,或者相反。這稱為完整運行特性

1.4 併發

兩個或多個“進程”同時執行就出現了併發。這裡的“進程”之所以打上引號,是因為這並不是電腦科學意義上的真正操作系統級進程。這是虛擬進程,或者任務,表示一個邏輯上相關的運算序列。

1.5 任務

在ES6中,有一個新的概念建立在事件迴圈隊列之上,叫作任務隊列。這個概念給大家帶來的最大影響可能是Promise的非同步特性。
事件迴圈隊列類似於一個游樂園游戲:玩過了一個游戲之後,你需要重新到隊尾排隊才能再玩一次。而任務隊列類似於玩過了游戲之後,插隊接著繼續玩。

2.回調

回調是編寫和處理JavaScript程式非同步邏輯的最常用方式。
回調函數是JavaScript的非同步主力軍,並且它們不辱使命地完成了自己的任務。

2.1 continuation

//A
ajax("...",function(data){
    //C
});
//B

//A//B表示程式的前半部分,而//C標識了程式的後半部分。前半部分立刻執行,然後是一段時間不確定的停頓。在未來的某個時刻,如果Ajax調用完成,程式就會從停下的位置繼續執行後半部分。

信任的問題

//C會延遲到將來發生,並且在第三方的控制下。我們把這稱為控制反轉,也就是把自己程式一部分的執行控制交給某個第三方。在你的代碼和第三方工具之間有一份並沒有明確表達的契約。

//過分信任輸入
function addNumbers(x,y){
    return x + y;
}

addNumbers(21,21);//42
addNumbers(21,"21");//"2121"
//針對不信任輸入的防禦性代碼
function addNumbers(x,y){
    if(typeof x != "number" || y != "number"){
        throw Error("Bad parameters");
    }
    return x + y;
}

addNumbers(21,21);//42
addNumbers(21,"21");//Error: "Bad parameters"
//依舊安全但更好一些
function addNumbers(x,y){
    x = Number(x);
    y = Number(y);
    
    return x + y;
}

addNumbers(21,21);//42
addNumbers(21,"21");//42

3.Promise

通過回調表達程式非同步和管理併發的兩個主要缺陷:缺乏順序性和可信任性。
我們用回調函數來封裝程式中的continuation,然後把回調交給第三方,期待其能夠調用回調,實現正確的功能。通過這種形式,我們要表達的意思是:“這是將來要做的事情,要在當前的步驟完成之後發生”。
如果我們不把自己程式的continuation傳給第三方,而是希望第三方給我們提供瞭解其任務何時結束的能力,然後我們自己的代碼來決定下一步做什麼。這種範式就稱為Promise
絕大多數JavaScript/DOM平臺新增的非同步API都是基於Promise構建的。

相關閱讀:看這一篇就夠了!淺談ES6的Promise對象

4.生成器

我們把註意力轉移到一種順序、看似同步的非同步流程式控制製表達風格。使這種風格成為可能的“魔法”就是ES6生成器(generator)

4.1 打破完整運行

var x = 1;

//下麵是生成器函數
function *foo(){
    x++;
    yield;//暫停點
    console.log("x: ",x);
}

function bar(){
    x++;
}

var it = foo();//構造迭代器

it.next();//啟動foo()
x;//2
bar();
x;//3
it.next();//x: 3

註意:function* foo(){...}function *foo(){...}是一樣的,唯一區別是*位置的風格不同。function*foo(){...}(沒有空格)也一樣,這隻是風格偏好問題。
上述代碼的運行過程:

  1. it = foo()運算並沒有執行生成器*foo(),而只是構造了一個迭代器(iterator),這個迭代器會控制它的執行。
  2. 第一個it.next()啟動了生成器*foo(),並運行了*foo()第一行的x++
  3. *foo()yield語句處暫停,在這一點上第一個it.next()調用結束。
  4. 我們查看x的值,此時為2。
  5. 我們調用bar(),它通過x++再次遞增x。
  6. 我們再次查看x的值,此時為3.
  7. 最後的it.next()調用從暫停處恢復了生成器*foo()的執行,並運行console.log(...)語句,這條語句使用當前x的值3。

相關閱讀:知乎上關於生成器的解釋

4.2 生成器+Promise

ES6中最完美的世界就是生成器(看似同步的非同步代碼)和Promise(可信任可組合)的組合。

獲得Promise和生成器最大效用的最自然的方法就是yield出來一個Promise,然後通過這個Promise來控制生成器的迭代器。

推薦閱讀:ECMAScript 6 入門

參考資料:《你不知道的JavaScript》(中捲) 第二部分 非同步和性能


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 在這裡呢,我們先來說下關於完美運動框架的封裝思路。 想讓一個物體運動呢,我們必須給那個物體加上定位屬性;其次想讓一個物體自動運動的話必須用到定時器;知道了這個後,基本上就差不多做完了(哈哈,給你個小安慰)! 首先在封裝框架之前我們得封裝一下獲取非行間樣式,這樣的話我們就不單單局限於(offsetwi ...
  • 現在前端全棧裡面有一種技術棧比較火 前端使用 vue 或者react 後端使用 koa2 mysql資料庫 或者mongdb做數據儲存 但是基本這樣的全棧教程 都要收費 收費就收費吧 但是 有沒有遇到非常好的教程 於是 準備硬著頭皮看別人項目的源碼 自己摸索 一步一步完成 koa + mongdb的 ...
  • 恢復內容開始 2018-05-17 17:49:11 通過<a>標簽來創建HTML文檔里的鏈接 有兩種使用 <a> 標簽的方式: 1. 通過使用 href 屬性 - 創建指向另一個文檔的鏈接或者網址; 2. 通過使用 name 屬性 - 創建文檔內的書簽。 代碼舉例: 1. <a herf = "h ...
  • 1. Vue.js 介紹 Vue,讀音 /vjuː/,類似於 view),是一套用於構建用戶界面的漸進式框架(重點在於視圖層)。 作者:尤雨溪 註:學習 vue.js 時,一定要拋棄 jQuery 的思想(jQuery的特點在於強大的選擇器、dom操作,而vue.js 的特點在於數據驅動來實現數據和 ...
  • 今天才知道原來position加上上下左右可以控制的到div的寬度的 比如我現在有一個需要 這樣一個需求 現在是紅色部分50px 但是你可能不知道剩下的藍色部分的高度是多少 藍色部分要怎麼填滿剩餘的高度呢 這時候left,right,top,bottom,就很有用了 直接給藍色的div:positi ...
  • 前言: 最近剛剛完成項目,空閑一段時間,想起之前有被問起怎麼對前端進行性能優化,自己也是腦中零零散散的總不成體系,現特來總結,歡迎補充指教。 1、整體資源 (1)js、css源碼壓縮 (2)css文件放到文檔頂部,js 文件放到文檔底部 因為瀏覽器渲染網頁是自上而下的,用戶第一眼見到的是頁面,先載入 ...
  • scoped css "官方文檔" scoped css可以直接在能跑起來的vue項目中使用。 使用方法: 使用scoped劃分本地樣式的結果編譯結果如下: 即在元素中添加了一個唯一屬性用來區分。 缺點 一、如果用戶在別處定義了相同的類名,也許還是會影響到組件的樣式。 二、根據css樣式優先順序的特性 ...
  • 一、 redux出現的動機 1. Javascript 需要管理比任何時候都要多的state2. state 在什麼時候,由於什麼原因,如何變化已然不受控制。3. 來自前端開發領域的新需求4. 我們總是將兩個難以理清的概念混淆在一起:變化和非同步。5. Redux 視圖讓state 的變化變得可預測。 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...