首頁  >  文章  >  web前端  >  JS非同步程式設計之Promise、Generator、async/await

JS非同步程式設計之Promise、Generator、async/await

不言
不言原創
2018-07-07 09:56:361665瀏覽

 這篇文章主要介紹了關於JS非同步程式設計之Promise、Generator、async/await ,有著一定的參考價值,現在分享給大家,有需要的朋友可以參考一下

JS非同步程式設計( 2) - Promise、Generator、async/await

上篇文章我們講了下JS非同步程式設計的相關知識,例如什麼是非同步,為什麼要使用非同步程式設計以及在瀏覽器中JS如何實現異步的。
最後我們捎帶講了幾種JS非同步程式設計模式(回調,事件和發布/訂閱模式),這篇我們繼續去深入了解下其他的幾種非同步程式設計模式。

Promise

Promise是ES6推出的一種非同步程式設計的解決方案。其實在ES6之前,許多非同步的工具庫就已經實現了各種類似的解決方案,而ES6將其寫進了語言標準,統一了用法。 Promise解決了回調等解決方案嵌套的問題並且使程式碼更加易讀,有種在寫入同步方法的既視感。

我們先來簡單了解下ES6中Promise的用法

var p = new Promise(function async(resolve, reject){    // 这里是你的异步操作
    setTimeout(function(){        if(true){
            resolve(val);
        }else{
            reject(error);
        }
    }, 1000)
})

p.then(function(val){    console.log('resolve');
}, function(){    console.log('reject');
})

首先,ES6規定Promise是個建構函數,其接受一個函數作為參數如上面程式碼中的async函數,此函數有兩個參數,resolve、reject分別對應成功失敗兩種狀態,我們可以選擇在不同時候執行resolve或reject去觸發下一個動作,執行then方法裡的函數。

我們可以簡單比較下回呼的寫法和promise的寫法的不同

對於傳統回呼寫法來說,一般會寫成這樣

asyncFn1(function () {
  asyncFn2(function() {
    asyncFn3(function() {        // xxxxx
    });
  });
});

或我們將各個回呼函數拆出來獨立來寫以減少耦合,像是這樣:

function asyncFn1(callback) {    return function() {        console.log('asyncFn1 run');
        setTimeout(function(){
            callback();
        }, 1000);
    }
}function asyncFn2(callback) {    return function(){        console.log('asyncFn2 run');
        setTimeout(function(){
            callback();
        }, 1000);
    }
}function normalFn3() {    console.log('normalFn3 run');
}

asyncFn1(asyncFn2(normalFn3))()

最後我們看下Promise的寫法

function asyncFn1() {    
console.log('asyncFn1 run');    
return new Promise(function(resolve, reject) {
        setTimeout(function(){
            resolve();
        }, 1000)
    })
}function asyncFn2() {    
console.log('asyncFn2 run');    
return new Promise(function(resolve, reject) {
        setTimeout(function(){
            resolve();
        }, 1000)
    })
}function normalFn3() {    console.log('normalFn3 run');
}

asyncFn1().then(asyncFn2).then(normalFn3);

這樣來看無論是第一種還是第二種寫法,都會讓人感到不是很直觀,而Promise的寫法更加直觀和語義化。

Generator

Generator函數也是ES6提供的一個特殊的函數,其語法行為與傳統函數完全不同。

我們先直觀看個Generator實際的用法

function* oneGenerator() {  
yield 'Learn';  
yield 'In';  
return 'Pro';
}var g = oneGenerator();

g.next();   // {value: "Learn", done: false}g.next();   // {value: "In", done: false}g.next();   // {value: "Pro", done: true}

Generator函數是一種特殊的函數,他有這麼多特點:

  • 宣告時需要在function後面加上*,並且配合函數裡面yield關鍵字來使用。

  • 在执行Generator函数的时候,其会返回一个Iterator遍历器对象,通过其next方法,将Generator函数体内的代码以yield为界分步执行

  • 具体来说当执行Generator函数时,函数并不会执行,而是需要调用Iterator遍历器对象的next方法,这时程序才会执行从头或者上一个yield之后到下一个yield或者return或者函数体尾部之间的代码,并且将yield后面的值,包装成json对象返回。就像上面的例子中的{value: xxx, done: xxx}

  • value取的yield或者return后面的值,否则就是undefined,done的值如果碰到return或者执行完成则返回true,否则返回false。

我们知道了简单的Generator函数的用法以后,我们来看下如何使用Generator函数进行异步编程。

首先我们先来看下使用Generator函数能达到怎样的效果。

// 使用Generator函数进行异步编程function* oneGenerator() {  yield asyncFn1();  yield asyncFn2();  yield normalFn3();
}// 我们来对比一下PromiseasyncFn1().then(asyncFn2).then(normalFn3);

我们可以看出使用Generator函数进行异步编程更像是在写同步任务,对比Promise少了很多次then方法的调用。

好,那么接下来我们就来看下如何实际使用Generator函数进行异步编程。

这里我要特别说明一下,事实上Generator函数不像Promise一样是专门用来解决异步处理而产生的,人们只是使用其特性来产出了一套异步的解决方案,所以使用Generator并不像使用Promise一样有一种开箱即用的感觉。其更像是在Promise或者回调这类的解决方案之上又封装了一层,让你可以像上面例子里一样去那么写。

我们还是具体来看下上面的例子,我们知道单写一个Generator是不能运行的对吧,我们需要执行他并且使用next方法来让他分步执行,那么什么时候去调用next呢?答案就是我们需要在异步完成时去调用next。我们来按照这个思路补全上面的例子。

var g;function asyncFn() {
    setTimeout(function(){
        g.next();
    }, 1000)
}function normalFn() {    console.log('normalFn run');
}function* oneGenerator() {  yield asyncFn();  return normalFn();
}

g = oneGenerator();

g.next();// 这里在我调用next方法的时候执行了asyncFn函数// 然后我们的希望是在异步完成时自动去再调用g.next()来进行下面的操作,所以我们必须在上面asyncFn函数体内的写上g.next(); 这样才能正常运行。// 但其实这样是比较奇怪的,因为当我定义asyncFn的时候其实是不知道oneGenerator执行后叫什么名儿的,即使我们提前约定叫g,但这样asyncFn就太过于耦合了,不仅写法很奇怪而且耦合太大不利于扩展和重用。反正总而言之这种写法很不好。

那么怎么解决呢,我们需要自己写个方法,能自动运行Generator函数,这种方法很简单在社区里有很多,最著名的就是大神TJ写的co模块,有兴趣的同学可以看下其源码实现。这里我们简单造个轮子:

// 如果我们想要去在异步执行完成时自动调用next就需要有一个钩子,回调函数的callback或者Promise的then。function autoGenerator(generator){  var g = generator();  function next(){    var res = g.next();  // {value: xxx, done: xxx}

    if (res.done) {        return res.value;
    }    if(typeof res.value === 'function'){    // 认为是回调
        res.value(next);
    }else if(typeof res.value === 'object' && typeof res.value.then === 'function'){     // 认为是promise
        res.value.then(function(){
            next();
        })
    }else{
        next();
    }
  }

  next();
}// ----function asyncFn1(){    console.log('asyncFn1');    return new Promise(function(resolve){
        setTimeout(function(){
            resolve();
        }, 1000)
    })
}function asyncFn2() {    console.log('asyncFn2');    return function(callback){
        setTimeout(function(){
            callback();
        }, 1000);
    }
}function normalFn() {    console.log('normalFn');
}function* oneGenerator() {  yield asyncFn1();  yield asyncFn2();  yield normalFn();
}

autoGenerator(oneGenerator);

这个方法我们简单实现了最核心的部分,有些判断可能并不严谨,但大家理解这个思路就可以了。有了这个方法,我们才可以方便的使用Generator函数进行异步编程。

Async/Await

如果你学会了Generator函数,对于Async函数就会很容易上手。你可以简单把Async函数理解成就是Generator函数+执行器。我们就直接上实例好了

function asyncFn1(){    console.log('asyncFn1');    return new Promise(function(resolve){
        setTimeout(function(){
            resolve('123');
        }, 2000)
    })
}function asyncFn2() {    console.log('asyncFn2');    return new Promise(function(resolve){
        setTimeout(function(){
            resolve('456');
        }, 2000)
    })
}

async function asyncFn () {    var a = await asyncFn1();    var b = await asyncFn2();    console.log(a,b)
}

asyncFn();// asyncFn1// asyncFn2// 123,456

当然async里实现的执行器肯定是跟咱们上面简单实现的有所不同,所以在用法上也会有些注意的点

  • 首先async函数的返回值是一个Promise对象,不像是generator函数返回的是Iterator遍历器对象,所以async函数执行后可以继续使用then等方法来继续进行下面的逻辑

  • await後面一般跟Promise對象,async函數執行時,遇到await後,等待後面的Promise對象的狀態從pending變成resolve的後,將resolve的參數返回並自動往下執行直到下一個await或結束

  • await後面也可以跟一個async函數進行嵌套使用。

對於異步來說,還有很多的知識點我們沒有講到,例如異常處理,多非同步並行執行等等,這篇和上篇文章主要還是希望大家對非同步程式有直覺的了解,清楚各種解決方案之間的差異與優劣。由於篇幅和精力有限,對於其他我們沒講到的知識點,如果大家有興趣有機會我會再寫文章深入講解的。

以上就是本文的全部內容,希望對大家的學習有所幫助,更多相關內容請關注PHP中文網!

相關推薦:

JS非同步程式設計的介紹

#React-Reflux的基礎介紹

以上是JS非同步程式設計之Promise、Generator、async/await的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn