在javascript單線程的世界裏,沒有異步步履維艱。本章節介紹異步編程的發展,從callback
,Events
到promise
,generator
,async/await
.javascript
在(javascript)單線程的世界裏,若是有多個任務,就必須排隊,前面一個完成,再繼續後面的任務。就像一個ATM排隊取錢似的,前面一個不取完,就不讓後面的取。 爲了這個問題,javascript提供了2種方式: 同步和異步。 異步就像銀行取錢填了單子約了號,等着叫到號,再去作取錢,等待的時間裏還能夠乾點其餘的事兒~html
舉個例子:經過api拿到數據,數據裏面有圖片,圖片加載成功渲染,那麼代碼以下:java
// 僞代碼
request(url, (data) => {
if(data){
loadImg(data.src, () => {
render();
})
}
})
複製代碼
若是有在業務邏輯比較複雜或者NodeJS I/O操做比較頻繁的時候,就成了下面這個樣子:node
doSth1((...args, callback) => {
doSth2((...args, callback) => {
doSth3((...args, callback) => {
doSth4((...args, callback) => {
doSth5((...args, callback) => {
})
})
})
})
})
複製代碼
這樣的維護性
和可讀性
,整我的瞬間感受很差了~jquery
try {
setTimeout(() => {
throw new Error('unexpected error');
}, 100);
} catch (e) {
console.log('error2', e.message);
}
複製代碼
以上代碼運行拋出異常,但try catch不能獲得將來時間段的異常。git
流程控制只能經過維護各類狀態
來處理,不利於管理es6
無論瀏覽器仍是NodeJS,提供了大量內置事件API來處理異步。github
瀏覽器中如: websocket
, ajax
, canvas
, img
,FileReader
等 NodeJS如: stream
, http
等web
EventEmitter
事件模型addEventListener
,此外瀏覽器也提供一些自定義事件的API,但兼容性很差,具體能夠這篇文章;也能夠用Node中的EventEmitter
;jquery
中也對此作了封裝,on
,bind
等方法支持自定義事件。事件必定程度上解決了解耦
和提高了代碼可維護性
;對於異常處理
,只有部分支持相似error事件
才能處理。若想實現異常處理機制,只有本身模擬error事件,比較繁瑣。ajax
Promise嚴格來講不是一種新技術,它只是一種機制,一種代碼結構和流程,用於管理異步回調。爲了統一規範產生一個Promise/A+規範,點擊查看Promise/A+中文版,cnode的William17
實現了Promise/A+規範,有興趣的能夠點這裏查看
promise
狀態由內部控制,外部不可變pending
到resovled
, rejected
,一旦進行完成不可逆then/catch
操做返回一個promise實例,能夠進行鏈式操做readFile(path1).then(function (data) {
console.log(data.toString());
return readFile(path2);
}).then(function (data) {
console.log(data.toString());
return readFile(errorPath);
}).then(function (data) {
console.log(data.toString());
}).catch(function (e) {
console.log('error', e);
return readFile(path1);
}).then(function (data) {
console.log(data.toString());
});
複製代碼
Promise的缺陷:
promise.catch
才能才能接收到Generator是ES6提供的方法,是生成器,最大的特色:能夠暫停執行和恢復執行(能夠交出函數的執行權),返回的是指針對象
.
const run = function (generator) {
var g = generator()
var perform = function (result) {
if (result.done === true) {
return result.value
}
if (isPromise(result.value)) {
return result.value.then(function (v) {
return perform(g.next(v))
}).catch(function (e) {
return perform(g.throw(e))
})
} else {
return perform(g.next(result.value))
}
}
return perform(g.next())
}
const isPromise = f => f.constructor === Promise
function* g() {
var a = yield sleep(1000, _ => 1)
var b = yield sleep(1000, _ => 2)
return a + b
}
function sleep(d, fn) {
return new Promise((resolve, reject) => {
setTimeout(_ => resolve(fn()), d)
})
}
複製代碼
因爲以上問題,因而一個叫 co庫誕生,支持thunk
和Promise
. 關於thunk能夠查看阮一峯老師的thunk介紹和應用
ES7提供了async
函數,使得異步操做變得更加方便。
yield
只能是promise
和thunk
實例代碼:
async function asyncReadFile() {
var p1 = readFile(path.join(__dirname, '../data/file1.txt'));
var p2 = readFile(path.join(__dirname, '../data/file2.txt'));
var [f1, f2] = await Promise.all([p1, p2]);
return `${f1.toString()}\n${f2.toString()}`;
}
(async function () {
try {
console.log(await asyncReadFile());
} catch (e) {
console.log(e.message)
}
})();
複製代碼
Node8.0
發佈,全面支持 async/await
, 推薦使用 async/await
, 低版本node能夠使用 babel
來編譯處理。 而 爲了方便 接口設計時 返回 promise
更方面使用者. 固然依然使用 callback
, 經過 promisify
作轉換, Node8.0
已經內置 util.promisify
方法。
異步編程
在javascript中扮演者重要的角色,雖然如今須要經過babel
,typescript
等編譯或轉換代碼,跟着規範
和標準
走,就沒有跑偏。
很久以前在github博客上的文章了。
如需轉載,請備註出處。