亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

JS異步處理的進化史深入講解

 更新時間:2019年08月25日 15:14:05   作者:banggan  
這篇文章主要給大家介紹了關于JS異步處理的相關資料,文中通過示例代碼介紹的非常詳細,對大家學習或者使用JS具有一定的參考學習價值,需要的朋友們下面來一起學習學習吧

前言

javascript是一門單線程的語言,也就是說一次只能完成一件任務,如果有多個任務,就需要排隊進行處理。如果一個任務耗時很長,后面的任務也必須排隊等待,這樣大大的影響了整個程序的執(zhí)行。為了解決這個問題,javascript語言將任務分為兩種模式:

  • 同步:當我們打開網(wǎng)站,網(wǎng)頁的頁面骨架渲染和頁面元素渲染,就是一大推同步任務。
  • 異步:我們在瀏覽新聞時,加載圖片或音樂之類占用資源大且耗時久的任務就是異步任務。

本文主要針對近兩年javascript的發(fā)展,主要介紹異步處理的進化史。目前,在javascript異步處理中,有以下幾種方式:

callback

回調函數(shù)是最早解決異步編程的方法。無論是常見的setTimeout還是ajax請求,都是采用回調的形式把事情在某一固定的時刻進行執(zhí)行。

 //常見的:setTimeout
 setTimeout(function callback(){
  console.log('aa');
 }, 1000);
 //ajax請求
 ajax(url,function callback(){
  console.log("ajax success",res);
 })

回調函數(shù)的處理一般將函數(shù)callback作為參數(shù)傳進函數(shù),在合適的時候被調用執(zhí)行。回調函數(shù)的優(yōu)點就是簡單、容易理解和實現(xiàn),但有個致命的缺點,容易出現(xiàn)回調地獄(Callback hell),即多個回調函數(shù)嵌套使用。造成代碼可讀性差、可維護性差且只能在回調中處理異常。

ajax(url, () => {
	//todo
	ajax(url1, () => {
		//todo
		ajax(url2, () => {
			//todo
		})
	})
})

事件監(jiān)聽

事件監(jiān)聽采用的是事件驅動的模式。事件的執(zhí)行不取決于代碼的順序,而是某個事件的發(fā)生。

假設有兩個函數(shù),為f1綁定一個事件(jQuery的寫法),當f1函數(shù)發(fā)生success事件時,執(zhí)行函數(shù)f2:

f1.on('success',f2);

對f1進行改寫:

function f1(){
	ajax(url,() => {
		//todo
		f1.trigger('success');//觸發(fā)success事件,從而執(zhí)行f2函數(shù)
	})
}

事件監(jiān)聽的方式較容易理解,可以綁定多個事件,每個事件可以指定多個回調函數(shù),而且可以"去耦合",有利于實現(xiàn)模塊化。缺點是整個程序都要變成事件驅動型,運行流程會變得很不清晰。閱讀代碼的時候,很難看出主流程。

發(fā)布訂閱

我們假定,存在一個"信號中心",某個任務執(zhí)行完成,就向信號中心"發(fā)布"(publish)一個信號,其他任務可以向信號中心"訂閱"(subscribe)這個信號,從而知道什么時候自己可以開始執(zhí)行。這就叫做 發(fā)布/訂閱模式(publish-subscribe pattern),又稱**觀察者模式"(observer pattern) **。

//利用jquery的插件實現(xiàn)
//首先,f2向消息中心訂閱success事件
jQuery.subscribe('success',f2);
//對f1進行改寫:
function f1(){
	ajax(url,() => {
		//todo
		jQuery.publish('success');//當f1執(zhí)行完畢后,向消息中心jQuery發(fā)布success事件,從而執(zhí)行f2函數(shù)
	})
}
//f2執(zhí)行完畢后,可以取消訂閱
jQuery.unsubscribe('success',f2)

該方法和事件監(jiān)聽的性質類似,但我們可以通過消息中心來查閱一共有多少個信號,每個信號有多少個訂閱者。

Promise

**Promise**是CommonJS工作組提出的一種規(guī)范,可以獲取異步操作的消息,也是異步處理中常用的一種解決方案。Promise的出現(xiàn)主要是用來解決回調地獄、支持多個并發(fā)的請求,獲取并發(fā)請求的數(shù)據(jù)并且解決異步的問題。

let p = new Promise((resolve, reject) => {
 //做一些異步操作
 setTimeout(()=>{
  let num = parseInt(Math.random()*100);
  	if(num > 50){
   resolve("num > 50"); // 如果數(shù)字大于50就調用成功的函數(shù),并且將狀態(tài)變成Resolved
  	}else{
   	reject("num <50");// 否則就調用失敗的函數(shù),將狀態(tài)變成Rejected
  }
	},10000)
});
p.then((res) => {
	console.log(res);
}).catch((err) =>{
 console.log(err);
})

Promise有三種狀態(tài):等待pending、成功fulfied、失敗rejected;狀態(tài)一旦改變,就不會再變化,在Promise對象創(chuàng)建后,會馬上執(zhí)行。等待狀態(tài)可以變?yōu)閒ulfied狀態(tài)并傳遞一個值給相應的狀態(tài)處理方法,也可能變?yōu)槭顟B(tài)rejected并傳遞失敗信息。任一一種情況出現(xiàn)時,Promise對象的 then 方法就會被調用(then方法包含兩個參數(shù):onfulfilled 和 onrejected,均為 Function。當Promise狀態(tài)為fulfilled時,調用 then 的 onfulfilled 方法,當Promise狀態(tài)為rejected時,調用 then 的 onrejected 方法)。

需要注意的是: Promise.prototype.then 和  Promise.prototype.catch 方法返回promise 對象, 所以可以被鏈式調用,如下圖:


Promise的方法:

  • Promise.all(iterable):誰執(zhí)行得慢,以誰為準執(zhí)行回調。返回一個promise對象,只有當iterable里面的所有promise對象成功后才會執(zhí)行。一旦iterable里面有promise對象執(zhí)行失敗就觸發(fā)該對象的失敗。對象在觸發(fā)成功后,會把一個包iterable里所有promise返回值的數(shù)組作為成功回調的返回值,順序跟iterable的順序保持一致;如果這個新的promise對象觸發(fā)了失敗狀態(tài),它會把iterable里第一個觸發(fā)失敗的promise對象的錯誤信息作為它的失敗錯誤信息。Promise.all方法常被用于處理多個promise對象的狀態(tài)集合。
  • Promise.race(iterable): 誰執(zhí)行得快,以誰為準執(zhí)行回調。iterable參數(shù)里的任意一個子promise被成功或失敗后,父promise馬上也會用子promise的成功返回值或失敗詳情作為參數(shù)調用父promise綁定的相應句柄,并返回該promise對象。
  • Promise.reject(err)與Promise.resolve(res)

Generators/yield

Generators是ES6提供的異步解決方案,其最大的特點就是可以控制函數(shù)的執(zhí)行??梢岳斫獬梢粋€內部封裝了很多狀態(tài)的狀態(tài)機,也是一個遍歷器對象生成函數(shù)。Generator 函數(shù)的特征:

  • function關鍵字與函數(shù)名之間有一個星號;
  • 函數(shù)體內部使用yield表達式,定義不同的內部狀態(tài);
    • 通過yield暫停函數(shù),next啟動函數(shù),每次返回的是yield表達式結果。next可以接受參數(shù),從而實現(xiàn)在函數(shù)運行的不同階段,可以從外部向內部注入不同的值。next返回一個包含value和done的對象,其中value表示迭代的值,后者表示迭代是否完成。

舉個例子:

function* createIterator(x) {
 let y = yield (x+1)
 let z = 2*(yield(y/3))
 return (x+y+z)
}
// generators可以像正常函數(shù)一樣被調用,不同的是會返回一個 iterator
let iterator = createIterator(4);
console.log(iterator.next()); // {value:5,done:false}
console.log(iterator.next()); // {value:NaN,done:false}
console.log(iterator.next()); // {value:NaN,done:true}
let iterator1 = createIterator(4);//返回一個iterator
//next傳參數(shù)
console.log(iterator1.next());  // {value:5,done:false}
console.log(iterator1.next(12)); // {value:4,done:false}
console.log(iterator1.next(15)); // {value:46,done:true}

代碼分析:

  • 當不參數(shù)時,next的value返回NaN;
  • 當傳參數(shù)時,作為上一個yeild的值,在第一次使用next時,傳參數(shù)無效,只有第二次開始,才有效。
  • 第一次執(zhí)行next時,函數(shù)會被暫停在yeild(x+1),所以返回的是4+1=5;
  • 第二次執(zhí)行next時,傳入的12為上一次yeild表達式的值,所以y=12,返回的是12/3=4;
  • 第三次執(zhí)行next時,傳入的15為上一次yeild表達式的值,所以z=30,y=12;x=4,返回30+12+4=46

async/await

初入async/await

async/await在ES7提出,是目前在javascript異步處理的終極解決方案。

async 其本質是 Generator 函數(shù)的語法糖。相較于Generator放入改進如下:

  • 內置執(zhí)行器:Generator 函數(shù)的執(zhí)行必須靠執(zhí)行器,而async函數(shù)自帶執(zhí)行器。其調用方式與普通函數(shù)一模一樣,不需要調next方法;
  • 更好的語義:async表示定義異步函數(shù),而await表示后面的表達式需要等待,相較于*和yeild更語義化;
  • 更廣的適用性:co模塊約定,yield命令后面只能是Thunk函數(shù)或 Promise對象。而 async 函數(shù)的await命令后面則可以是Promise 或者 原始類型的值;
  • 返回Promise:async 函數(shù)返回值是Promise對象,比 Generator函數(shù)返回的 Iterator對象方便,可以直接使用 then() 方法進行鏈式調用;

語法分析

async語法

用來定義異步函數(shù),自動將函數(shù)轉換為promise對象,可以使用then來添加回調,其內部return的值作為then回調的參數(shù)。

async function f(){
	return "hello async";
}
f().then((res) => {  //通過then來添加回調且內部返回的res作為回調的參數(shù)
	console.log(res);  // hello async
})

在異步函數(shù)的內部可以使用await,其返回的promise對象必須等到內部所以await命令后的promise對象執(zhí)行完,才會發(fā)生狀態(tài)變化即執(zhí)行then回調。

const delay = function(timeout){ 
	return new Promise(function(resolve){
		return setTimeout(resolve, timeout);
 });
}
async function f(){
  await delay(1000);
  await delay(2000);
  return '完成';
}
f().then(res => console.log(res));//需要等待3秒之后才會打?。和瓿?/pre>

await即表示異步等待,用來暫停異步函數(shù)的執(zhí)行,只能在異步函數(shù)和promise使用,且當使用在promise前面,表示等待promise完成并返回結果。

async function f() {
  return await 1  //await后面不是Promise的話,也會被轉換為一個立即為resolve的promise
};
f().then( res => console.log("處理成功",res))//打印出:處理成功 1
	 .catch(err => console.log("處理是被",err))////打印出:Promise{<resolved>:undefined}

錯誤處理

如果await后面的異步出現(xiàn)錯誤,等同于async返回的promise對象為reject,其錯誤會被catch的回調函數(shù)接收到。需要注意的是,當 async 函數(shù)中只要一個 await 出現(xiàn) reject 狀態(tài),則后面的 await 都不會被執(zhí)行。

let a;
async function f(){
  await Promise.reject("error")
  a = await 1    //該await并沒有執(zhí)行 
}
err().then(res => console.log(a))

怎么處理呢,可以把第一個await放在try/catch,遇到函數(shù)的時候,可以將錯誤拋出并往下執(zhí)行。

async function f() { 
  try{ 
    await Promise.reject('error');  
  }catch(error){
    console.log(error);
  }
  return await 1
}
f().then(res => console.log('成功', res))//成功打印出1

如果有多個await處理,可以統(tǒng)一放在try/catch模塊中,而且async可以使得try/catch同時處理同步和異步錯誤。

總結

通過以上六種javascript異步處理的常用方法,可以看出async/await可以說是異步終極解決方案了,最后看一下async/await用得最多的場景:

如果一個業(yè)務需要很多個異步操作組成,并且每個步驟都依賴于上一步的執(zhí)行結果,這里采用不同的延時來體現(xiàn):

//首先定義一個延時函數(shù)
function delay(time) {
  return new Promise(resolve => {
    setTimeout(() => resolve(time), time);
  });
}
//采用promise鏈式調用實現(xiàn)
delay(500).then(result => {
  return delay(result + 1000)
}).then(result => {
  return delay(result + 2000)
}).then(result => {
  console.log(result)  //3500ms后打印出3500
}).catch(error => {
  console.log(error)
}) 
//采用async實現(xiàn)
async function f(){
 const r1 = await delay(500)
 const r2 = await delay(r1+1000)
 const r3 = await delay(r2+2000)
 return r3
}
f().then(res =>{
 console.log(res)
}).catch(err=>{
 console.log(err)
})

可以看出,采用promise實現(xiàn)采用了很多then進行不停的鏈式調用,使得代碼變得冗長和復雜且沒有語義化。而 async/await首先使用同步的方法來寫異步,代碼非常清晰直觀,而且使代碼語義化,一眼就能看出代碼執(zhí)行的順序,最后 async 函數(shù)自帶執(zhí)行器,執(zhí)行的時候無需手動加載。

總結

以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,謝謝大家對腳本之家的支持。

相關文章

  • 微信小程序實現(xiàn)多個按鈕的顏色狀態(tài)轉換

    微信小程序實現(xiàn)多個按鈕的顏色狀態(tài)轉換

    這篇文章主要為大家詳細介紹了微信小程序實現(xiàn)多個按鈕的顏色狀態(tài)轉換,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-02-02
  • 微信小程序多音頻播放進度條問題

    微信小程序多音頻播放進度條問題

    小程序的音頻組件居然沒有進度控制的功能,需要我們自己實現(xiàn),下面腳本之家小編給大家?guī)砹宋⑿判〕绦蚨嘁纛l播放進度條問題,感興趣的朋友一起看看吧
    2018-08-08
  • Mozilla 表達式 __noSuchMethod__

    Mozilla 表達式 __noSuchMethod__

    這是一個很特殊的方法,但是其存在的意義很大。不過很可惜只有firefox支持了。一個簡單的例子解釋一下它的用處
    2009-04-04
  • JS實現(xiàn)閃動的title消息提醒效果

    JS實現(xiàn)閃動的title消息提醒效果

    這篇文章主要介紹了JS實現(xiàn)閃動的title消息提醒效果,考慮并兼容了大部份的瀏覽器,需要的朋友可以參考下
    2014-06-06
  • javascript URL編碼和解碼使用說明

    javascript URL編碼和解碼使用說明

    在使用url進行參數(shù)傳遞時,經(jīng)常會傳遞一些中文名的參數(shù)或URL地址,在后臺處理時會發(fā)生轉換錯誤。
    2010-04-04
  • Bootstrap每天必學之標簽與徽章

    Bootstrap每天必學之標簽與徽章

    Bootstrap每天必學之標簽與徽章,對Bootstrap標簽與徽章小編也了解的很少,希望通過這篇文章和大家更多的去學習Bootstrap標簽與徽章,從中得到收獲。
    2015-11-11
  • 詳解JavaScript中怎么實現(xiàn)鏈表

    詳解JavaScript中怎么實現(xiàn)鏈表

    鏈表是一系列節(jié)點串聯(lián)形成的數(shù)據(jù)結構,鏈表存儲有序的元素集合,鏈表中的元素在內存中并不是連續(xù)放置的,本文給大家介紹了在JavaScript中怎么實現(xiàn)鏈表,需要的朋友可以參考下
    2023-12-12
  • JavaScript提高網(wǎng)站性能優(yōu)化的建議(二)

    JavaScript提高網(wǎng)站性能優(yōu)化的建議(二)

    這篇文章主要介紹了JavaScript提高網(wǎng)站性能優(yōu)化的建議(二)的相關資料,需要的朋友可以參考下
    2016-07-07
  • JavaScript表單驗證完美代碼

    JavaScript表單驗證完美代碼

    用原生JS寫一個簡單的表單驗證功能,代碼分為html部分和js部分,代碼簡單易懂,非常不錯,具有參考借鑒價值,需要的朋友參考下
    2017-03-03
  • 鼠標劃過實現(xiàn)延遲加載并隱藏層的js代碼

    鼠標劃過實現(xiàn)延遲加載并隱藏層的js代碼

    鼠標劃過延遲加載隱藏層的效果,想必大家都有見到過吧,在本文將為大家詳細介紹下使用js是如何實現(xiàn)的,感興趣的朋友可以參考下
    2013-10-10

最新評論