異步
- 不連續(xù)的執(zhí)行纯赎,就叫做異步。相應(yīng)地励稳,連續(xù)的執(zhí)行就叫做同步佃乘。
- 通常異步是處理一些耗時的操作。
回想在ES6沒出現(xiàn)之前驹尼,需要異步處理事件的時候大多數(shù)情況下就是回調(diào)函數(shù)趣避,可能還是是一層一層的callback嵌套,代碼混亂邏輯不清晰新翎,如果還有錯誤處理程帕,那代碼就更加冗雜讓人不想去讀第二遍住练!
回調(diào)
通過函數(shù)參數(shù)傳遞到其他代碼里的,某一塊可以執(zhí)行的引用愁拭。
容易引發(fā)的問題
1.信任問題
2.同步or異步 不確定讲逛?
Promise對象
所謂Promise,簡單說就是一個容器岭埠,里面保存著某個未來才會結(jié)束的事件(通常是一個異步操作)的結(jié)果盏混。
- Promise 是一個對象,從它可以獲取異步操作的消息惜论。Promise對象代表一個異步操作许赃,有三種狀態(tài):pending(進(jìn)行中)、fulfilled(已成功)和rejected(已失敼堇唷)混聊。
構(gòu)建Promise對象
var promise = new Promise(function(resolve, reject) {
// ... some code
if (/* 異步操作成功 */){
resolve(value);
}
else {
reject(error);
}
});
promise的構(gòu)造函數(shù)傳入一個回調(diào),該回調(diào)接受兩個回調(diào)函數(shù)resolve和reject蹦掐。
resolve函數(shù)的作用是技羔,將Promise對象的狀態(tài)從“未完成”變?yōu)椤俺晒Α保磸?pending 變?yōu)?resolved),在異步操作成功時調(diào)用卧抗,并將異步操作的結(jié)果藤滥,作為參數(shù)傳遞出去;reject函數(shù)的作用是社裆,將Promise對象的狀態(tài)從“未完成”變?yōu)椤笆 保磸?pending 變?yōu)?rejected)拙绊,在異步操作失敗時調(diào)用,并將異步操作報出的錯誤泳秀,作為參數(shù)傳遞出去标沪。
Promise實例生成以后,可以用then方法分別指定resolved狀態(tài)和rejected狀態(tài)的回調(diào)函數(shù)嗜傅。
promise.then(function(value) {
// success
}, function(error) {
// failure
});
Promise 提供then方法加載回調(diào)函數(shù)金句,catch方法捕捉執(zhí)行過程中拋出的錯誤。
Promise.resovle方法吕嘀,可以將不是Promise對象作為參數(shù)违寞,返回一個Promise對象。
有兩種情形:
1.假設(shè)傳入的參數(shù)沒有一個.then方法偶房,那么這個返回的Promise對象變成了resolve狀態(tài)趁曼,其resolve的值就是這個對象本身。
2.假設(shè)傳入的參數(shù)帶有一個then方法(稱為thenable對象), 那么將這個對象的類型變?yōu)镻romise,其then方法變成Promise.prototype.then方法棕洋。
不管是then還是catch方法調(diào)用挡闰,都返回一個新的promise對象;其實then方法的調(diào)用,只不過是注冊了完成態(tài)和失敗態(tài)下的回調(diào)函數(shù)而已摄悯。這些回調(diào)函數(shù)組成一個回調(diào)隊列赞季,處理resolve的值。
Q 鏈?zhǔn)秸{(diào)用的then和非鏈?zhǔn)秸{(diào)用的有什么區(qū)別射众?
var promise1 = new Promise(function(resolve){
resolve(1);
});
var thenPromise = promise1.then(function(value){
console.log(value);
});
var catchPromise = thenPromise.catch(function(error){
console.log(error);
});
console.log(promise1 !== thenPromise); // true
console.log(thenPromise !== catchPromise); //true
情況1
var promise1 = new Promise(function(resolve){
resolve(1);
});
promise1.then(function(value){
return value * 2;
});
promise1.then(function(value){
return value * 2;
});
promise1.then(function(value){
console.log("1"+value);
});
情況2
var promise1 = new Promise(function(resolve){
resolve(2);
});
promise1.then(function(value){
return value * 2;
}).then(function(value){
return value * 2;
}).then(function(value){
console.log("1"+value);
});
Generator 函數(shù)
整個 Generator 函數(shù)就是一個異步任務(wù)的容器碟摆。
Generator 函數(shù)除了狀態(tài)機(jī),還是一個遍歷器對象生成函數(shù)叨橱。返回的遍歷器對象典蜕,可以依次遍歷 Generator 函數(shù)內(nèi)部的每一個狀態(tài)。
Generator 函數(shù)是一個普通函數(shù)罗洗,但是有兩個特征愉舔。一是,function關(guān)鍵字與函數(shù)名之間有一個星號伙菜;二是轩缤,函數(shù)體內(nèi)部使用yield表達(dá)式,定義不同的內(nèi)部狀態(tài)贩绕。
調(diào)用 Generator 函數(shù)火的,返回一個遍歷器對象,代表 Generator 函數(shù)的內(nèi)部指針淑倾。每次調(diào)用遍歷器對象的next方法馏鹤,內(nèi)部指針就從函數(shù)頭部或上一次停下來的地方開始執(zhí)行,直到遇到下一個yield表達(dá)式(或return語句)為止娇哆,會返回一個有著value和done兩個屬性的對象湃累,value屬性表示當(dāng)前的內(nèi)部狀態(tài)的值,是yield表達(dá)式后面那個表達(dá)式的值碍讨;done屬性是一個布爾值治力,表示是否遍歷結(jié)束。
function* gen(x) {
var y = yield x + 2;
return y;
}
var g = gen(1);
g.next() // { value: 3, done: false }
g.next() // { value: undefined, done: true }
- Generator 函數(shù)的一個重要實際意義就是用來處理異步操作勃黍,改寫回調(diào)函數(shù)宵统。
- Generator 函數(shù)可以暫停執(zhí)行和恢復(fù)執(zhí)行,這是它能封裝異步任務(wù)的根本原因覆获。
- 除此之外榜田,它還有兩個特性,使它可以作為異步編程的完整解決方案:函數(shù)體內(nèi)外的數(shù)據(jù)交換和錯誤處理機(jī)制锻梳。
function* asyncJob(){
// ...其他代碼
varf=yieldreadFile(fileA);
// ...其他代碼
}
上面代碼的函數(shù)asyncJob是一個協(xié)程,它的奧妙就在其中的yield命令净捅。它表示執(zhí)行到此處疑枯,執(zhí)行權(quán)將交給其他協(xié)程。
協(xié)程遇到y(tǒng)ield命令就暫停蛔六,等到執(zhí)行權(quán)返回荆永,再從暫停的地方繼續(xù)往后執(zhí)行废亭。
它的最大優(yōu)點,就是代碼的寫法非常像同步操作具钥。
async函數(shù)
async函數(shù)就是將 Generator 函數(shù)的星號(*)替換成async豆村,將yield替換成await
async告訴這個函數(shù)里面有await
async function getTitle(url) {
let response = await fetch(url);
let html = await response.text();
return html.match(/<title>([\s\S]+)<\/title>/i)[1];
}
getTitle('https://tc39.github.io/ecma262/').then(console.log)
- async函數(shù)返回一個 Promise 對象。
- async函數(shù)內(nèi)部return語句返回的值骂删,會成為then方法回調(diào)函數(shù)的參數(shù)掌动。async函數(shù)內(nèi)部拋出錯誤,會導(dǎo)致返回的 Promise 對象變?yōu)閞eject狀態(tài)宁玫。拋出的錯誤對象會被catch方法回調(diào)函數(shù)接收到粗恢。
- 如果await后面的異步操作出錯,那么等同于async函數(shù)返回的 Promise 對象被reject欧瘪。拋出的錯誤對象會被catch方法回調(diào)函數(shù)接收到眷射。
await拿到的東西就是promise resolve之后的值
async 函數(shù)的實現(xiàn)原理,就是將 Generator 函數(shù)和自動執(zhí)行器佛掖,包裝在一個函數(shù)里妖碉。
function spawn(genF) {
return new Promise(function(resolve, reject) {
var gen = genF();
function step(nextF) {
try {
var next = nextF();
} catch (e) {
return reject(e);
}
if (next.done) {
return resolve(next.value);
}
Promise.resolve(next.value).then(function(v) {
step(function() { return gen.next(v); });
}, function(e) {
step(function() { return gen.throw(e); });
});
}
step(function() { return gen.next(undefined); });
});
}
比較
一個異步展示動畫的程序
// Promise寫法
function chainAnimationPromise(elem, animations) {
var ret = null;
var p = Promise.resolve();
for (var anim of animations) {
p = p.then(function(val) {
ret = val;
return anim(elem);
});
}
return p.catch(function(e) {
}).then(function() {
return ret;
});
}
// Generator寫法
function chainAnimationPromise(elem, animations) {
return spawn(function* () {
var ret = null;
try {
for (var anim of animations) {
ret = yield anim(elem);
}
} catch (error) {
}
return ret;
});
}
// async寫法
async function chainAnimationPromise(elem, animations) {
var ret = null;
try {
for(var anim of animations) {
ret = await anim(elem);
}
} catch (error) {
}
return ret;
}