九色国产,午夜在线视频,新黄色网址,九九色综合,天天做夜夜做久久做狠狠,天天躁夜夜躁狠狠躁2021a,久久不卡一区二区三区

打開APP
userphoto
未登錄

開通VIP,暢享免費(fèi)電子書等14項(xiàng)超值服

開通VIP
5分種讓你了解javascript異步編程的前世今生,從onclick到await/async
javascript與異步編程

為了避免資源管理等復(fù)雜性的問題, javascript被設(shè)計(jì)為單線程的語言,即使有了html5 worker,也不能直接訪問dom.

javascript 設(shè)計(jì)之初是為瀏覽器設(shè)計(jì)的GUI編程語言,GUI編程的特性之一是保證UI線程一定不能阻塞,否則體驗(yàn)不佳,甚至界面卡死。

一般安卓開發(fā),會有一個界面線程,一個后臺線程,保證界面的流暢。

由于javascript是單線程,所以采用異步非阻塞的編程模式,javascript的絕大多數(shù)api都是異步api.

本文是本人的一個總結(jié):從Brendan Eich剛設(shè)計(jì)的初版javascript到現(xiàn)在的ES6,一步步總結(jié)javascript異步編程歷史。

說明:

請安裝babel環(huán)境,用babel-node 執(zhí)行對應(yīng)例子

什么是異步編程

那么什么是異步編程,異步編程簡單來說就是:執(zhí)行一個指令不會馬上返回結(jié)果而執(zhí)行下一個任務(wù),而是等到特定的事件觸發(fā)后,才能得到結(jié)果。

以下是當(dāng)有ABC三個任務(wù),同步或異步執(zhí)行的流程圖:

示意圖來自stackoverflow

同步

thread ->|----A-----||-----B-----------||-------C------|

異步

A-Start ---------------------------------------- A-End | B-Start ----------------------------------------|--- B-End | | C-Start -------------------- C-End | | V V V V V V thread-> |-A-|---B---|-C-|-A-|-C-|--A--|-B-|--C--|---A-----|--B--|

顯然,在宏觀上,同步程序是串行的執(zhí)行各任務(wù),執(zhí)行單個任務(wù)時會阻塞純線程,異步可以“并行”的執(zhí)行任務(wù)。

異步編程時就需要指定異步任務(wù)完成后需要執(zhí)行的指令,總的來說有以下幾種“指定異步指令”的方式:

  1. 屬性
  2. 回調(diào)
  3. Promise
  4. Generator
  5. await,async

下面會一步一步展現(xiàn)各種方式。

屬性

每個編程語言對異步實(shí)現(xiàn)的方式不一樣,C#可以用委托,java可以用接口或基類傳入的方式,

早期的javascript的異步的實(shí)現(xiàn)也類似于這種類的屬性的方式:每個類實(shí)例的相關(guān)回調(diào)事件有相應(yīng)的handler(onclick,onchange,onload等)。

在DOM0級事件處理程序,就是將一個函數(shù)賦值給一個元素的屬性。

element.onclick=function{ alert('clicked');}window.onload=function{ alert('loaded');}
問題

這種寫法簡單明了,同時會有以下幾個問題

  • 耦合度高
    所有的事件處理都需要寫的一個函數(shù)中:
window.onload=function{ handlerA; handlerB; handlerc; }

如果這三個handler來自三個不同的模塊,那這個文件模塊耦合度就為3(華為的計(jì)算方法)。依賴高,不利于復(fù)用和維護(hù)。

  • 不安全,容易被重寫
window.onload=function{ console.log('handler 1');}//... 很多其它框架,庫,主題 的代碼var handlerbak=window.onloadwindow.onload=function{ handlerbak; //這行注釋的話上面handler 1就會被覆蓋。 console.log('handler 2');}

當(dāng)代碼量大時,這種問題沒有warning也沒有error, 經(jīng)驗(yàn)不豐富的前端可能花費(fèi)大量的時間查找問題。

事件handler容易被重寫,庫/框架的安全,寄托于使用者的對框架的熟練程度,極不安全。

回調(diào)(發(fā)布/訂閱)

由于javascript支持函數(shù)式編程,JavaScript語言對異步編程的實(shí)現(xiàn)可以用回調(diào)函數(shù)。

DOM2級事件解決了這個問題以上兩個問題

element.addEventListener('click',function{ alert('clicked');})

這里實(shí)際上是一個發(fā)布訂閱模式,addEventListener相當(dāng)于subscribe, dispatchEvent相當(dāng)于publish, 很好的解決了訂閱者之前的依賴,jquery,vue,flux,angularjs均實(shí)現(xiàn)了類似的模式。

發(fā)布訂閱模式雖解決了上面耦合和不安全的問題,但是在實(shí)現(xiàn)大型應(yīng)用時,還會有以下問題。

問題
  • 回調(diào)黑洞 多層回調(diào)嵌套,代碼可讀性差。

    step1(function (value1) { step2(value1, function(value2) { step3(value2, function(value3) { step4(value3, function(value4) { // Do something with value4 }); }); }); });
  • 異常無法捕捉

try{ setTimeout(function{ JSON.parse('{'a':'1'}') console.log('aaaa') },0)}catch(ex){ console.log(ex); //不能catch到這個異常}
  • 流程控制(異步代碼,同步執(zhí)行)

當(dāng)C操作依賴于B操作和C操作,而B與A沒有依賴關(guān)系時,不用第三方庫(如async,eventproxy)的話,B與A本可以并行,卻串行了,性能有很大的提升空間。 流程圖如下:

graph LRStart-->AA-->BB-->C

但用promise后,可以方便的用并行:

Promise:

graph LRStart-->AStart-->BA-->CB-->C
Promise(ECMAScript5)

如上流程圖,Promise很好的解決了“并行”的問題,我們看看用promise庫怎么發(fā)送get請求:

import fetch from 'node-fetch'fetch('https://api.github.com/users/etoah').then((res)=>res.json).then((json)=>console.log('json:',json))

可以看到promise把原來嵌套的回調(diào),改為級連的方式了,實(shí)際是一種代理(proxy)。

var promise = new Promise(function(resolve, reject) { // 異步操作的代碼 if (/* 異步操作成功 */){ resolve(value); } else { reject(error); }});

promise把成功和失敗分別代理到resolved 和 rejected .

同時還可以級連catch異常。

新建一個promise實(shí)例:

到這里異步的問題,有了一個比較優(yōu)雅的解決方案了,如果要吹毛求疵,還有一些別扭的地方,需要改進(jìn)。

問題

封裝,理解相對回調(diào)復(fù)雜,這是以下我公司項(xiàng)目的一段代碼(coffeescript),并發(fā)代碼,加上resolved,rejected的回調(diào), 即使是用了coffee,混個業(yè)務(wù)和參數(shù)處理,第一眼看上去還是比較懵,代碼可讀性并沒有想象中的好。

#并發(fā)請求companyLevel companyInfoP = companyinfoServicesP.companyLevel({hid: req.session.hid}) requestP(userOption).success((userInfo)-> roleOption = uri: '#{config.server_host}/services/rights/userroles?userid=#{user.userId}' method: 'GET' #保證companyInfo 寫入 Q.all([companyInfoP, requestP(roleOption)]).spread( (companyinfo, roles)-> Util.session.init req, user, roles.payload Util.session.set(req, 'companyInfo', companyinfo.payload) Util.session.set(req, 'roleids', roles.payload) u = Util.session.getInfo req return next {data: Util.message.success(u)} , (err)-> return next err )

在指明resolved 和 rejected的時,用的還是最原始的回調(diào)的方式。

能不能用同步的方式寫異步代碼?

在ES5前是這基本不可實(shí)現(xiàn),但是,ES6的語法引入了Generator, yeild的關(guān)鍵字可以用同步的語法寫異步的程序。

Generator(ECMAScript6)

簡單來說generators可以理解為一個可遍歷的狀態(tài)機(jī)。 語法上generator,有兩個特征:

  1. function 關(guān)鍵字與函數(shù)名之前有一個星號。
  2. 函數(shù)體內(nèi)部使用yield關(guān)鍵字,定義不同的內(nèi)部狀態(tài)。

由于generator是一個狀態(tài)機(jī),所以需要手動調(diào)用next 才能執(zhí)行,但TJ大神開發(fā)了co模塊,可以自動執(zhí)行g(shù)enerator。

import co from 'co';co(function* { var now = Date.now; yield sleep(150); //約等待150ms console.log(Date.now - now);});function sleep(ms){ return function(cb){ setTimeout(cb, ms); };}import fetch from 'node-fetch'co(function* { let result= yield [ (yield fetch('https://api.github.com/users/tj')).json, (yield fetch('https://api.github.com/users/etoah')).json, ]; console.log('result:',result)});

無論是延遲執(zhí)行,還是并發(fā)的從兩個接口獲取數(shù)據(jù),generator都可以用同步的方式編寫異步代碼。

注意:co模塊約定,yield命令后面只能是Thunk函數(shù)或Promise對象

問題
  • 需要手動執(zhí)行
    即使用了TJ的CO模塊,不是標(biāo)準(zhǔn)的寫法,感覺用hack解決問題
  • 不夠直觀,沒有語義化。
await,async(ECMAScript7)

ES7 引入了像C#語言中的 await,async關(guān)鍵字,而且babel已支持(引入plugins:transform-async-to-generator )

async函數(shù)完全可以看作多個異步操作,包裝成的一個Promise對象,而await命令就是內(nèi)部then命令的語法糖。

import fetch from 'node-fetch';(async function { let result= await fetch('https://api.github.com/users/etoah'); let json =await result.json; console.log('result:',json);});//exception(async function { try{ let result= await fetch('https://api.3github.com/users/etoah'); let json =await result.json; console.log('result:',json); } catch(ex){ console.warn('warn:',ex); }})

簡單比較會發(fā)現(xiàn),async函數(shù)就是將Generator函數(shù)的星號(*)替換成async,將yield替換成await,同時不需要co模塊,更加語義化。

但是與yeild又不完全相同,標(biāo)準(zhǔn)沒有接收await*的語法( :,

若需“并行”執(zhí)行promise數(shù)組,推薦用Promise.All,所以需要并行請求時,需要這樣寫:

(async function { let result= await Promise.all([ (await fetch('https://api.github.com/users/tj')).json, (await fetch('https://api.github.com/users/etoah')).json ]); console.log('result:',result);});

雖說沒有不能用 await* , 總體來說結(jié)構(gòu)還是簡單清晰的

沒有任何callback,流程和異常捕獲是完全同步的寫法。而且javascript語言級別支持這種寫法。可以說這是異步的終極解決方案了。

總結(jié)

到這里,jser結(jié)合promise,yield,await的寫法,可以和回調(diào)嵌套說拜拜了。

雖有這么多的不同的異步編程方式,但是異步編程的本質(zhì)并沒有變,只有對coder更友好了而已,但對工程化可讀性和可維護(hù)性有很大的改進(jìn)。

全文完,如有不嚴(yán)謹(jǐn)?shù)牡胤剑瑲g迎指正。

本站僅提供存儲服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊舉報(bào)。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
javascript異步編程的前世今生
Async/Await替代Promise的6個理由 | Fundebug博客
async/await 是如何讓代碼更加簡潔的?
目前最好的 JavaScript 異步方案 async/await
如何在現(xiàn)代JavaScript中編寫異步任務(wù)
理解 JavaScript 的 async/await
更多類似文章 >>
生活服務(wù)
熱點(diǎn)新聞
分享 收藏 導(dǎo)長圖 關(guān)注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服