咱們常常會在編程時遇到異步的需求,
異步
,就是說一個任務不是能夠連續完成,他須要被分紅前後執行,先執行一部分,而後程序能夠轉執行別的任務,待執行完了前一部分,在回頭執行後一部分,那麼咱們曾經使用的哪些方法實現過呢,這裏咱們介紹一下:es6
1.回調函數
2.Promise
3.async await
複製代碼
舉一個例子:咱們對一個文件的讀取和修改操做編程
fs.readFile('./ziyi.text', (content) => {
setTimeout(() => {
content += '123';
fs.append('./ziyi.text', content, (err) => {
......
})
}, 3000)
})
複製代碼
這裏咱們經過回調函數,能夠在讀取文件三秒後,進行文件內容的修改,在回調裏面又有回調實現異步,可是這就陷入了回調地獄
,因此咱們後面異步的實現就出現了鏈式調用的Promise
app
Promise('./ziyi.text')
.then(() => {
content += '123';
})
.then(() => {
fs.append('./ziyi.text')
})
.then()
......
複製代碼
這樣鏈式調用看上去咱們的代碼就像串聯同樣,再也不是多個回調函數嵌套時,Promise雖然跳出了異步嵌套的怪圈,用鏈式表達更加清晰,可是咱們也發現若是有大量的異步請求的時候,流程複雜的狀況下,會發現充滿了屏幕的then,看起來很是吃力,而ES7的Async/Await
的出現就是爲了解決這種複雜的狀況。。異步
async () => {
let c = await fs.readFile('./ziyi.text')
c += '123';
let res = await fs.append('./ziyi.text', c);
}
複製代碼
這樣咱們的代碼閱讀起來就很清晰了,每一步作的事都簡介明瞭,其實,在Promise到async以前,做者TJ
寫了一的Co
的庫優化了Promise寫法,這個優化使用了es6裏Generator
,大概的寫法以下:yield
表示暫停,看上去是否是和咱們如今用的async await很像async
co(
function * test() {
let c = yield fs.readFile('./ziyi.text');
c += '123';
let res = yield fs.append('./ziyi.text', c);
}
)
複製代碼
generator
(生成器)是ES6標準引入的新的數據類型。一個generator看上去像一個函數,但能夠返回屢次。函數
以前的Promise恢復了異步回調的可信任性,而generator正是以一種看似順序、同步的方式實現了異步控制流程,加強了代碼可讀性。優化
generator和函數不一樣的是,generator由function*定義,而且,除了return語句,還能夠用yield返回屢次。它的執行由next()方法來一步一步執行ui
舉個例子:spa
function foo() {
// setTimeout(() => {
// console.log(1);
// }, 2000)
// return 'foo';
return new Promise((resolve) => {
setTimeout(() => {
resolve(123);
}, 2000)
})
}
function* test() { //generator
console.log('start');
let a = yield foo(); // 賦值語句從右往左<——,因此執行到foo()會中止
console.log('middle')
let b = yield 2;
console.log('end');
}
let g = test();
console.log(g)
console.log(g.next()); //執行第一部分,代碼開始到第一個yield
複製代碼
let g = test();
咱們先把text()執行結果拿到,在它上面由next()方法執行generator裏的代碼
g.next()
經過第一次調用next,咱們執行到了foo(),g.next()返回的對象key由value和done,value爲yield 後面的內容,done表示是否還有next能夠執行,這裏done爲false,則generator未執行完3d
function* test() {
console.log('start');
let a = yield foo();
console.log('a', a);
let b = yield 2;
console.log('b', b);
console.log('end');
}
let g = test();
console.log(g)
console.log(g.next());
console.log(g.next());
console.log(g.next());
複製代碼
同理,咱們把後面兩步也執行,這時咱們的generator就執行完了。
可是咱們的a,b 未被賦到值,generator規定在next()
方法傳參能夠給上一個(注意是上一個)yield返回值,代碼以下
console.log(g.next());
console.log(g.next('A_value'));
console.log(g.next('B_value'));
複製代碼
賦值成功,a、b雖然被賦值了,可是這樣的賦值是不對的,咱們應該將yield後面的foo()和2給a,b。
console.log(g.next());
console.log(g.next(foo()));
console.log(g.next(2));
複製代碼
function generateAutoRun(gen) {
let g = gen();
function next(value) {
let res = g.next(value); //執行next()
if (res.done) return; //結束
next(res.value); //從第二個next開始,給上一個的yield返回值
}
next();
}
generateAutoRun(test);
複製代碼
在賦值時,咱們把上一個上一個next()對象的
value
值給yield的返回,這樣a就返回了foo()執行的結果,b就爲2。
介紹完了generator,咱們就可能夠用它來實現一下咱們async了
咱們定義兩個異步的 Promise
前後執行
let p1 = new Promise((resolve) => {
console.log('p1先');
setTimeout(() => {
console.log(1);
resolve(1)
}, 2000)
})
let p2 = new Promise((resolve) => {
console.log('p2後');
setTimeout(() => {
console.log(2);
resolve(2)
}, 1000)
})
function* test() {
let a = yield p1;
console.log(a,'a---')
let b = yield p2;
console.log(b,('b---'));
}
複製代碼
這時咱們就要實現p1,p2的前後執行了
function asyncTogenerate(gen) {
let g = gen();
function step(value) { //遞歸調用next
// 處理 yield 返回值問題
let info = g.next(value); //一步一步執行p1,p2,並給a,b 賦值p1,p2
if (info.done) { //執行完return掉
return;
} else {
// 把 yield 後面的東西(info.value) 直接 resolve,傳給下一個step,給a,b賦值
Promise.resolve(info.value).then((res) => {
// 下一個 yield 下一個 遞歸
step(res);
})
}
}
step(); //遞歸開始
}
asyncTogenerate(test);
複製代碼
Promise的then
方法,能夠保證前後執行,因此咱們上面代碼給每個yield返回的值進行Promise包裝一下
Promise.resolve(info.value).then((res) => { //把value包裝,使用它的then方法異步執行
複製代碼
看結果
正如咱們想要的同樣,p1先執行,而後再是p2,輸出結果也沒問題,由於p1是1秒後執行,p2是2秒後執行,因此先輸出2,再輸出1。
a爲p1返回的1,,b爲p2返回的2,賦值也沒有錯誤。
咱們常說什麼async/await
的出現淘汰了Promise
,能夠說是大錯特錯,偏偏相反,上面的例子說明,正由於有了Promise,纔有了改良版的async/await,二者是能夠相輔相成的
因此咱們想學好async/await,應該先去了解 Promise