es6異步函數

異步與同步

異步: 所謂異步,簡單來說就是 不能連續執行,上一個任務沒運行完,下一個任務照樣運行,任務之間不能連續,不能首尾相連。
同步: 同步則與異步相反,是 連續執行 的,下一個任務緊接着上一個任務之後運行,如果上一個任務沒有運行完,下一個任務沒法運行。

異步的解決方案

1、回調函數

當我們需要在執行的函數中進行其他的操作,就需要用到回調函數了。如:

function fn(args, callback){
	//TODO...
	callback();
}

fn(args, ()=> {
	//TODO...
})

2、事件監聽

如DOM事件的監聽、鼠標鍵盤的事件監聽等,都是異步執行的。

document.addEventListener("click", (e)=>{
	//TODO...
}, false)

3、發佈/訂閱

比如我要開一家新公司,供用戶加入,當我在新公司發佈消息時,只有註冊用戶才能收到消息。
我們架構程序時,我們可以從使用出發,比如先寫我們要怎麼用,如:

// 首先要開新公司
const company = new Company();

// 然後需要有些用戶吧,比如用戶A、B等等
// 用戶A
const userA = function(msg){
	console.log(`用戶A接收到的消息爲:${msg}`)
}

// 用戶B
const userB = function(msg){
	console.log(`用戶B接收到的消息爲:${msg}`)
}

// 再接着把這些用戶註冊到新公司裏(他們就是這公司的註冊用戶了)
company.addSubscribe(userA, userB);

// 這樣我就可以以公司的名義向用戶發送消息了吧
company.release('今天公司成立,註冊用戶每人獎勵一萬元整。');

然後接下來我們是不是要考慮怎麼實現這個Company啦(就是註冊這公司有些什麼樣的手續及後續的功能)。如:

// 首先我們是不是要定義一個這樣的類啊
class Company {
	constructor(){
		// 在類裏面我們要定義一個存儲用戶數據的變量
		this.users = [];
	}
	// 前面我們用到的添加用戶的功能
	addSubscribe(...args){
		args.forEach((val) => {
            if(!this.users.includes(val)){
                this.users.push(val);
            }
        })

        return this;
	}
	// 還需要發佈消息的功能吧
	release(msg){
		this.users.forEach((fn) => {
            fn(msg);
        })

        return this;
	}	
}

4、Promise(es6新增)

es6新增的異步處理,如果按第1項裏的回調函數,如果多層調用會很麻煩,如ajax:

// 比如我們要先獲取token,再根據token去獲取用戶信息,然後根據用戶信息獲取其他的內容等等
// 就得這樣(這樣一直嵌套感覺很不爽)
ajax(option, (res) => {
	// 拿到token
	ajax(option, (res) => {
		// 拿到用戶信息
		ajax(option, (res) => {
			// 拿到其他等等數據
		})
	})
})

那麼在es6裏面我們可以怎麼做呢,Promise的語法什麼的我就不說了,我只講思路。
如:

// 封裝一個ajax方法
function myAjax(option){
	return new Promise((resolve, reject) => {
		ajax(option, (res) => {
			resolve(res);
		}, (err) => {
			reject(err);
		})
	});
}

// 此時我用這個方法去完成上面的需求
myAjax(option).then(res => {
	// 拿到token
	return myAjax(res.token);
}).then(res => {
	// 拿到用戶信息
	return myAjax(res.info);
}).then(res => {
	// 拿到其他等等數據
	console.log(res);
})

generator函數(es6新增)

簡單來說,她是個可控制運行的函數,函數體內的內容是可控的。你叫她往下運行她就往下運行。來看一個簡單的例子:

function* idMaker(){
    let index = 0;
    while(true)
        yield index++;
}

let gen = idMaker(); // 返回 generator 對象
// gen.next()方法,返回 yield 表達式產生的值
console.log(gen.next().value); // 0
console.log(gen.next().value); // 1
console.log(gen.next().value); // 2
// ...

有人就會問了,平時咋用啊,貌似也用不到吧。那我們就結合上面Ajax,來實現一個類似需求,我們經常會遇到在一個內容很多的頁面上,我們是不是需要先加載什麼後加載什麼呢,比如我們得先把菜單列表的數據請求過來,再去請求新聞啊、活動啊之類的,對吧。那麼我們來看看怎麼玩。

// 這裏我們直接用上面封裝的myAjax方法
// 首先我們要定義一個generator函數
function* gen(){
	yield myAjax(option); // 請求菜單
	yield myAjax(option); // 請求新聞
	yield myAjax(option); // 請求活動
}

let g = gen();
g.next().value.then(res => {
	console.log('菜單請求成功');
	return g.next().value; // 菜單請求成功之後,接着往下請求
}).then(res => {
	console.log('新聞請求成功');
	return g.next().value; // 新聞請求成功之後,接着往下請求
}).then(res => {
	console.log('活動請求成功');
})
// 看完是不是感覺跟前面的Promise一樣啊,對,沒錯,就是一樣的。唯一不同的是什麼呢,我們把請求的參數放上一塊了(option放到了gen函數裏面了),這樣就方便代碼管理啦。

但是呢,每次都要手機調用,也很麻煩,再說了,generator語言化也不怎麼友好,比如“*”號啥意思?“yield”關鍵字又是啥意思?感覺不符合場景。有更好的東東嗎?當然有,請接着往下看!

async、await(es6新增)

沒錯,就是她了,字面意思已經告訴你了 “async”異步的意思,“await”等待。那她怎麼用呢,看例子:

// 這裏我們還是直接用上面封裝的myAjax方法
// 定義一個異步函數
async function fn(){
	let token = await myAjax(option); // 拿到token
	let info = await myAjax(token); // 根據token去拿用戶信息.
	let other = await myAjax(info); // 根據info去拿其他信息
}
// 最後我們再調用這個函數
fn() // async函數,返回的是一個Promise對象,你看哪兒哪兒都有Promise,是不是很重要
// 那麼既然是一個Promise對象,可不可以.then 或 .catch啊,當然可以
async function fn2(){
	return 'allen'
}
fn2().then(res => {
	console.log(res); // allen
})
// 這樣是不是方便很多,語義化也很好啊。

這裏有幾個點需要注意:
1、await只能放在async函數中。
2、await後面可以是任何對象
3、async函數返回的是一個Promise對象
4、如果await後面的Promise狀態變爲reject,那麼會停止整個async函數

總結

總的來說,以後常用的估計就是Promise與async函數、await的配合使用了,當然我們首先得了解Promise,瞭解異步。謝謝大家。

注:愛護原創,轉載請說明出處

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章