<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Python 協(xié)程與 JavaScript 協(xié)程的對(duì)比

          共 8131字,需瀏覽 17分鐘

           ·

          2021-09-15 13:51


          劇照:《眷思量》

          作者:從零開始的程序員生活

          來源:https://www.cnblogs.com/lgjbky/p/14759463.html

          前言

          以前沒怎么接觸前端,對(duì) JavaScript 的異步操作不了解,現(xiàn)在有了點(diǎn)了解。一查發(fā)現(xiàn) Python 和 JavaScript 的協(xié)程發(fā)展史簡(jiǎn)直就是一毛一樣!

          這里大致做下橫向?qū)Ρ群涂偨Y(jié),便于對(duì)這兩個(gè)語言有興趣的新人理解和吸收。

          共同訴求

          • 隨著 cpu 多核化,都需要實(shí)現(xiàn)由于自身歷史原因(單線程環(huán)境)下的并發(fā)功能
          • 簡(jiǎn)化代碼,避免回調(diào)地獄,關(guān)鍵字支持
          • 有效利用操作系統(tǒng)資源和硬件:協(xié)程相比線程,占用資源更少,上下文更快

          什么是協(xié)程?

          總結(jié)一句話,協(xié)程就是滿足下面條件的函數(shù):

          • 可以暫停執(zhí)行(暫停的表達(dá)式稱為暫停點(diǎn))
          • 可以從掛起點(diǎn)恢復(fù)(保留其原始參數(shù)和局部變量)
          • 事件循環(huán)是異步編程的底層基石

          混亂的歷史

          Python 協(xié)程的進(jìn)化

          • Python2.2 中,第一次引入了生成器
          • Python2.5 中,yield 關(guān)鍵字被加入到語法中
          • Python3.4 時(shí)有了 yield from(yield from 約等于 yield + 異常處理 + send), 并試驗(yàn)性引入的異步 I/O 框架 asyncio(PEP 3156)
          • Python3.5 中新增了 async/await 語法(PEP 492)
          • Python3.6 中 asyncio 庫(kù)"轉(zhuǎn)正" (之后的官方文檔就清晰了很多)

          在主線發(fā)展過程中,也出現(xiàn)了很多支線的協(xié)程實(shí)現(xiàn)如 Gevent。

          def foo():
              print("foo start")
              a = yield 1
              print("foo a", a)
              yield 2
              yield 3
              print("foo end")


          gen = foo()
          # print(gen.next())
          # gen.send("a")
          # print(gen.next())
          # print(foo().next())
          # print(foo().next())

          # 在python3.x版本中,python2.x的g.next()函數(shù)已經(jīng)更名為g.__next__(),使用next(g)也能達(dá)到相同效果。
          # next()跟send()不同的地方是,next()只能以None作為參數(shù)傳遞,而send()可以傳遞yield的值.

          print(next(gen))
          print(gen.send("a"))
          print(next(gen))
          print(next(foo()))
          print(next(foo()))

          list(foo())

          """
          foo start
          1
          foo a a
          2
          3
          foo start
          1
          foo start
          1
          foo start
          foo a None
          foo end
          """

          JavaScript 協(xié)程的進(jìn)化

          • 同步代碼
          • 異步 JavaScript: callback hell
          • ES6 引入 Promise/a+, 生成器 Generators(語法 function foo(){}* 可以賦予函數(shù)執(zhí)行暫停/保存上下文/恢復(fù)執(zhí)行狀態(tài)的功能), 新關(guān)鍵詞 yield 使生成器函數(shù)暫停。
          • ES7 引入 async函數(shù)/await語法糖,async 可以聲明一個(gè)異步函數(shù)(將 Generator 函數(shù)和自動(dòng)執(zhí)行器,包裝在一個(gè)函數(shù)里),此函數(shù)需要返回一個(gè) Promise 對(duì)象。await 可以等待一個(gè) Promise 對(duì)象 resolve,并拿到結(jié)果

          Promise 中也利用了回調(diào)函數(shù)。在 then 和 catch 方法中都傳入了一個(gè)回調(diào)函數(shù),分別在 Promise 被滿足和被拒絕時(shí)執(zhí)行,這樣就就能讓它能夠被鏈接起來完成一系列任務(wù)。

          總之就是把層層嵌套的 callback 變成 .then().then()...,從而使代碼編寫和閱讀更直觀。

          生成器 Generator 的底層實(shí)現(xiàn)機(jī)制是協(xié)程 Coroutine。

          functionfoo({
              console.log("foo start")
              a = yield 1;
              console.log("foo a", a)
              yield 2;
              yield 3;
              console.log("foo end")
          }

          const gen = foo();
          console.log(gen.next().value); // 1
          // gen.send("a") // http://www.voidcn.com/article/p-syzbwqht-bvv.html SpiderMonkey引擎支持 send 語法
          console.log(gen.next().value); // 2
          console.log(gen.next().value); // 3
          console.log(foo().next().value); // 1
          console.log(foo().next().value); // 1

          /*
          foo start
          1
          foo a undefined
          2
          3
          foo start
          1
          foo start
          1
          */

          Python 協(xié)程成熟體

          可等待對(duì)象可以在 await 語句中使用,可等待對(duì)象有三種主要類型:協(xié)程(coroutine), 任務(wù)(task) 和 Future。

          協(xié)程(coroutine)

          • 協(xié)程函數(shù):定義形式為 async def 的函數(shù);
          • 協(xié)程對(duì)象:調(diào)用 協(xié)程函數(shù) 所返回的對(duì)象
          • 舊式基于 generator(生成器)的協(xié)程

          任務(wù)(Task 對(duì)象):

          • 任務(wù) 被用來“并行的”調(diào)度協(xié)程, 當(dāng)一個(gè)協(xié)程通過 asyncio.create_task() 等函數(shù)被封裝為一個(gè) 任務(wù),該協(xié)程會(huì)被自動(dòng)調(diào)度執(zhí)行
          • Task 對(duì)象被用來在事件循環(huán)中運(yùn)行協(xié)程。如果一個(gè)協(xié)程在等待一個(gè) Future 對(duì)象,Task 對(duì)象會(huì)掛起該協(xié)程的執(zhí)行并等待該 Future 對(duì)象完成。當(dāng)該 Future 對(duì)象 完成,被打包的協(xié)程將恢復(fù)執(zhí)行。
          • 事件循環(huán)使用協(xié)同日程調(diào)度: 一個(gè)事件循環(huán)每次運(yùn)行一個(gè) Task 對(duì)象。而一個(gè) Task 對(duì)象會(huì)等待一個(gè) Future 對(duì)象完成,該事件循環(huán)會(huì)運(yùn)行其他 Task、回調(diào)或執(zhí)行 IO 操作。
          • asyncio.Task 從 Future 繼承了其除 Future.set_result() 和 Future.set_exception() 以外的所有 API。

          未來對(duì)象(Future):

          • Future 對(duì)象用來鏈接 底層回調(diào)式代碼 和高層異步/等待式代碼。
          • 不用回調(diào)方法編寫異步代碼后,為了獲取異步調(diào)用的結(jié)果,引入一個(gè) Future 未來對(duì)象。Future 封裝了與 loop 的交互行為,add_done_callback 方法向 epoll 注冊(cè)回調(diào)函數(shù),當(dāng) result 屬性得到返回值后,會(huì)運(yùn)行之前注冊(cè)的回調(diào)函數(shù),向上傳遞給 coroutine。

          幾種事件循環(huán)(event loop):

          • libevent/libev:Gevent(greenlet + 前期 libevent,后期 libev)使用的網(wǎng)絡(luò)庫(kù),廣泛應(yīng)用;
          • tornado:tornado 框架自己實(shí)現(xiàn)的 IOLOOP;
          • picoev:meinheld(greenlet+picoev)使用的網(wǎng)絡(luò)庫(kù),小巧輕量,相較于 libevent 在數(shù)據(jù)結(jié)構(gòu)和事件檢測(cè)模型上做了改進(jìn),所以速度更快。但從 github 看起來已經(jīng)年久失修,用的人不多。
          • uvloop:Python3 時(shí)代的新起之秀。Guido 操刀打造了 asyncio 庫(kù),asyncio 可以配置可插拔的event loop,但需要滿足相關(guān)的 API 要求,uvloop 繼承自 libuv,將一些低層的結(jié)構(gòu)體和函數(shù)用 Python 對(duì)象包裝。目前 Sanic 框架基于這個(gè)庫(kù)

          例子

          import asyncio
          import time


          async def exec():
              await asyncio.sleep(2)
              print('exec')

          # 這種會(huì)和同步效果一直
          # async def go():
          #     print(time.time())
          #     c1 = exec()
          #     c2 = exec()
          #     print(c1, c2)
          #     await c1
          #     await c2
          #     print(time.time())

          # 正確用法
          async def go():
              print(time.time())
              await asyncio.gather(exec(),exec()) # 加入?yún)f(xié)程組統(tǒng)一調(diào)度
              print(time.time())

          if __name__ == "__main__":
              asyncio.run(go())

          JavaScript 協(xié)程成熟體

          Promise 繼續(xù)使用

          Promise 本質(zhì)是一個(gè)狀態(tài)機(jī),用于表示一個(gè)異步操作的最終完成 (或失敗), 及其結(jié)果值。它有三個(gè)狀態(tài):

          • pending: 初始狀態(tài),既不是成功,也不是失敗狀態(tài)。
          • fulfilled: 意味著操作成功完成。
          • rejected: 意味著操作失敗。

          最終 Promise 會(huì)有兩種狀態(tài),一種成功,一種失敗,當(dāng) pending 變化的時(shí)候,Promise 對(duì)象會(huì)根據(jù)最終的狀態(tài)調(diào)用不同的處理函數(shù)。

          async、await語法糖

          async、await 是對(duì) Generator 和 Promise 組合的封裝,使原先的異步代碼在形式上更接近同步代碼的寫法,并且對(duì)錯(cuò)誤處理/條件分支/異常堆棧/調(diào)試等操作更友好。

          js 異步執(zhí)行的運(yùn)行機(jī)制

          1. 所有任務(wù)都在主線程上執(zhí)行,形成一個(gè)執(zhí)行棧。
          2. 主線程之外,還存在一個(gè)"任務(wù)隊(duì)列"(task queue)。只要異步任務(wù)有了運(yùn)行結(jié)果,就在"任務(wù)隊(duì)列"之中放置一個(gè)事件。
          3. 一旦"執(zhí)行棧"中的所有同步任務(wù)執(zhí)行完畢,系統(tǒng)就會(huì)讀取"任務(wù)隊(duì)列"。那些對(duì)應(yīng)的異步任務(wù),結(jié)束等待狀態(tài),進(jìn)入執(zhí)行棧并開始執(zhí)行。

          遇到同步任務(wù)直接執(zhí)行,遇到異步任務(wù)分類為宏任務(wù)(macro-task)和微任務(wù)(micro-task)。

          當(dāng)前執(zhí)行棧執(zhí)行完畢時(shí)會(huì)立刻先處理所有微任務(wù)隊(duì)列中的事件,然后再去宏任務(wù)隊(duì)列中取出一個(gè)事件。同一次事件循環(huán)中,微任務(wù)永遠(yuǎn)在宏任務(wù)之前執(zhí)行。

          例子

          var sleep = function (time{
              console.log("sleep start")
              return new Promise(function (resolve, reject{
                  setTimeout(function ({
                      resolve();
                  }, time);
              });
          };

          async function exec({
              await sleep(2000);
              console.log("sleep end")
          }

          async function go({
              console.log(Date.now())
              c1 = exec()
              console.log("-------1")
              c2 = exec()
              console.log(c1, c2)
              await c1;
              console.log("-------2")
              await c2;
              console.log(c1, c2)
              console.log(Date.now())
          }

          go();

          event loop 將任務(wù)劃分:

          • 主線程循環(huán)從"任務(wù)隊(duì)列"中讀取事件
          • 宏隊(duì)列(macro task)js 同步執(zhí)行的代碼塊,setTimeout、setInterval、XMLHttprequest、setImmediate、I/O、UI rendering等,本質(zhì)是參與了事件循環(huán)的任務(wù)
          • 微隊(duì)列(micro task)Promise、process.nextTick(node環(huán)境)、Object.observe, MutationObserver等,本質(zhì)是直接在 Javascript 引擎中的執(zhí)行的沒有參與事件循環(huán)的任務(wù)

          擴(kuò)展閱讀 Node.js 中的 EventLoop (http://www.ruanyifeng.com/blog/2014/10/event-loop.html)

          總結(jié)與對(duì)比

          說明pythonJavaScript點(diǎn)評(píng)
          進(jìn)程單進(jìn)程單進(jìn)程一致
          中斷/恢復(fù)yield,yield from,next,sendyield,next基本相同,但 JavaScript 對(duì) send 沒啥需求
          未來對(duì)象(回調(diào)包裝)FuturesPromise解決 callback,思路相同
          生成器generatorGenerator將 yield 封裝為協(xié)程Coroutine,思路一樣
          成熟后關(guān)鍵詞async、awaitasync、await關(guān)鍵詞支持,一毛一樣
          事件循環(huán)asyncio 應(yīng)用的核心。事件循環(huán)會(huì)運(yùn)行異步任務(wù)和回調(diào),執(zhí)行網(wǎng)絡(luò) IO 操作,以及運(yùn)行子進(jìn)程。asyncio 庫(kù)支持的 API 較多,可控性高基于瀏覽器環(huán)境基本是黑盒,外部基本無法控制,對(duì)任務(wù)有做優(yōu)先級(jí)分類,調(diào)度方式有區(qū)別這里有很大區(qū)別,運(yùn)行環(huán)境不同,對(duì)任務(wù)的調(diào)度先后不同,Python 可能和 Node.js 關(guān)于事件循環(huán)的可比性更高些,這里還需需要繼續(xù)學(xué)習(xí)

          到這里就基本結(jié)束了,看完不知道你會(huì)有什么感想,如有錯(cuò)誤還請(qǐng)不吝賜教。




          推薦閱讀:

          入門: 最全的零基礎(chǔ)學(xué)Python的問題  | 零基礎(chǔ)學(xué)了8個(gè)月的Python  | 實(shí)戰(zhàn)項(xiàng)目 |學(xué)Python就是這條捷徑


          干貨:爬取豆瓣短評(píng),電影《后來的我們》 | 38年NBA最佳球員分析 |   從萬眾期待到口碑撲街!唐探3令人失望  | 笑看新倚天屠龍記 | 燈謎答題王 |用Python做個(gè)海量小姐姐素描圖 |碟中諜這么火,我用機(jī)器學(xué)習(xí)做個(gè)迷你推薦系統(tǒng)電影


          趣味:彈球游戲  | 九宮格  | 漂亮的花 | 兩百行Python《天天酷跑》游戲!


          AI: 會(huì)做詩(shī)的機(jī)器人 | 給圖片上色 | 預(yù)測(cè)收入 | 碟中諜這么火,我用機(jī)器學(xué)習(xí)做個(gè)迷你推薦系統(tǒng)電影


          小工具: Pdf轉(zhuǎn)Word,輕松搞定表格和水印! | 一鍵把html網(wǎng)頁(yè)保存為pdf!|  再見PDF提取收費(fèi)! | 用90行代碼打造最強(qiáng)PDF轉(zhuǎn)換器,word、PPT、excel、markdown、html一鍵轉(zhuǎn)換 | 制作一款釘釘?shù)蛢r(jià)機(jī)票提示器! |60行代碼做了一個(gè)語音壁紙切換器天天看小姐姐!


          年度爆款文案


          點(diǎn)閱讀原文,領(lǐng)AI全套資料

          瀏覽 28
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  91在线无码精品秘 入口楼乃 | 日本www在线中文字幕 | aⅴ中文字幕不卡在线无码 | 国产一级a毛一级a看免费视频黑人 | 国产精品人妻无码久久 |