成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專(zhuān)欄INFORMATION COLUMN

淺談async·await

Magicer / 1081人閱讀

摘要:在語(yǔ)言中,函數(shù)替換的不是表達(dá)式,而是多參數(shù)函數(shù),將其替換成一個(gè)只接受回調(diào)函數(shù)作為參數(shù)的單參數(shù)函數(shù)。為什么里面必須使用函數(shù)呢,因?yàn)槲覀冃枰_保傳入的值只有一個(gè),利用其回調(diào)函數(shù),來(lái)進(jìn)行遞歸自動(dòng)控制函數(shù)的流程,接收和交還程序的執(zhí)行權(quán)

前言

這篇文章主要是梳理一下自己對(duì)阮一峰大神寫(xiě)的關(guān)于async/await文章,有寫(xiě)得不對(duì)的地方以及理解得不對(duì)的地方,各位大佬請(qǐng)指錯(cuò)!

對(duì)比

簡(jiǎn)單對(duì)比傳統(tǒng)異步promise異步,async異步

下文都會(huì)以setTimeout來(lái)進(jìn)行異步展示,方便理解。

傳統(tǒng)的回調(diào)

setTimeout(callback,1000);

function callback(){
    console.log("拿到結(jié)果了!");
}

setTimeout函數(shù)傳入了兩個(gè)參數(shù)(1000/callback),setTimeout被調(diào)用的時(shí)候,主線(xiàn)程不會(huì)等待1秒,而是先執(zhí)行別的任務(wù)。callback這個(gè)函數(shù)就是一個(gè)回調(diào)函數(shù),即當(dāng)1秒后,主線(xiàn)程會(huì)重新調(diào)用callback(這里也不再啰嗦去說(shuō)event Loop方面的知識(shí)了);

那么,當(dāng)我們異步函數(shù)需要嵌套的時(shí)候呢。比如這種情況:

setTimeout(function(){
    console.log("第一個(gè)異步回調(diào)了!")
    setTimeout(function(){
        console.log("第二個(gè)異步回調(diào)了!")
        setTimeout(function(){
            console.log("第三個(gè)異步回調(diào)了!")
            setTimeout(function(){
                console.log("第四個(gè)異步回調(diào)了!")
                setTimeout(function(){
                    console.log("第五個(gè)異步回調(diào)了!")
                },1000);
            },1000);
        },1000);
    },1000);
},1000);

OK,想死不?

我們用promise來(lái)處理

function timeout(ms) {
  return new Promise((resolve, reject) => {
    setTimeout(resolve, ms, "finish");
  });
}

timeout(2000)
  .then(value => {
    console.log("第一層" + value);
    return timeout(2000);
  })
  .then(value => {
    console.log("第二層" + value);
    return timeout(2000);
  })
  .then(value => {
    console.log("第三層" + value);
    return timeout(2000);
  })
  .then(value => {
    console.log("第四層" + value);
    return timeout(2000);
  })
  .then(value => {
    console.log("第五層" + value);
    return timeout(2000);
  })
  .catch(err => {
    console.log(err);
  });

OK,好看點(diǎn)了!

但是還是不方便!

我們用async/await來(lái)處理:

function timeout(ms) {
  return new Promise((resolve, reject) => {
    setTimeout(resolve, ms, "finish");
  });
}
async function asyncTimeSys(){
    await timeout(1000);
    console.log("第一層異步結(jié)束!")
    await timeout(1000);
    console.log("第二層異步結(jié)束!")
    await timeout(1000);
    console.log("第三層異步結(jié)束!")
    await timeout(1000);
    console.log("第四層異步結(jié)束!")
    await timeout(1000);
    console.log("第五層異步結(jié)束!")
    return "all finish";
}
asyncTimeSys().then((value)=>{
    console.log(value);
});

OK,舒服了!

在這個(gè)asyncTimeSys函數(shù)里面,所有的異步操作,寫(xiě)的跟同步函數(shù)沒(méi)有什么兩樣!

async的原型

async函數(shù)到底是什么?其實(shí)他就是Genarator函數(shù)(生成器函數(shù))的語(yǔ)法糖而已!

內(nèi)置執(zhí)行器。

Generator 函數(shù)的執(zhí)行必須靠執(zhí)行器,所以才有了co模塊,而async函數(shù)自帶執(zhí)行器。也就是說(shuō),async函數(shù)的執(zhí)行,與普通函數(shù)一模一樣。完全不像 Generator 函數(shù),需要調(diào)用next方法,或者用co模塊,才能真正執(zhí)行,得到最后結(jié)果。

更好的語(yǔ)義。

async和await,比起星號(hào)和yield,語(yǔ)義更清楚了。async表示函數(shù)里有異步操作,await表示緊跟在后面的表達(dá)式需要等待結(jié)果。

更廣的適用性。

co模塊約定,yield命令后面只能是 Thunk 函數(shù)或 Promise 對(duì)象,而async函數(shù)的await命令后面,可以是 Promise 對(duì)象和原始類(lèi)型的值(數(shù)值、字符串和布爾值,但這時(shí)等同于同步操作)。

返回值是 Promise。

async函數(shù)的返回值是 Promise 對(duì)象,這比 Generator 函數(shù)的返回值是 Iterator 對(duì)象方便多了。你可以用then方法指定下一步的操作。

進(jìn)一步說(shuō),async函數(shù)完全可以看作多個(gè)異步操作,包裝成的一個(gè) Promise 對(duì)象,而await命令就是內(nèi)部then命令的語(yǔ)法糖。

其實(shí),async函數(shù)就是一個(gè)由Generator封裝的異步環(huán)境,其內(nèi)部是通過(guò)交換函數(shù)執(zhí)行權(quán),以及thunk函數(shù)來(lái)實(shí)現(xiàn)的!

用Generator函數(shù)封裝異步請(qǐng)求

OK,我們簡(jiǎn)單的封裝一個(gè):

function timeout(ms) {
  return new Promise((resolve, reject) => {
    setTimeout(resolve, ms, "finish");
  });
}

function *times(){
    let result =yield timeout(1000);
    return "second next"
}

let gen = times();    //拿到生成器函數(shù),gen可以理解為指針
let firstYield = gen.next(); //firstYield此時(shí)為gen指針指向的第一個(gè)yield右邊的表達(dá)式,此時(shí)timeout(1000)被執(zhí)行
console.log(firstYield);    //   firstYield = {value:Pomise,done:false};

//接下來(lái)就是將firstYield中的value里的promise拿出來(lái),作為正常的Promise調(diào)用,如下:
firstYield.value.then(()=>{
    //當(dāng)timeout異步結(jié)束之后,執(zhí)行以下代碼,再將gen指針執(zhí)行下一個(gè)yield,由于以下沒(méi)有yield了,所以gen.next()的value為return里的東西
    console.log("timeout finish");
    console.log(gen.next());    //{value: "second next", done: true}
}).catch((err)=>{

});

這樣封裝有什么用呢,yield返回回來(lái)的東西,還是得像promise那樣調(diào)用。

我們先來(lái)看看同步的代碼,先讓它長(zhǎng)得像async和await那樣子:

function* times() {
  yield console.log(1);
  yield console.log(2);
  yield console.log(3);
  return "second next";
}

let gen = times();

let result = gen.next();

while (!result.done) {
    result = gen.next();
}

OK,非常像了,但是,這是同步的。異步請(qǐng)求必須得等到第一個(gè)yield執(zhí)行完成之后,才能去執(zhí)行第二個(gè)yield。我們?nèi)绻某僧惒?,肯定?huì)造成無(wú)限循環(huán)。

那么,times生成器里面如果都是異步的話(huà),我們應(yīng)該怎么調(diào)用呢?

function timeout(ms) {
  return new Promise((resolve, reject) => {
    setTimeout(resolve, ms, "finish");
  });
}

function *times(){
    yield timeout(2000);
    yield timeout(2000);
    yield timeout(2000);
    return "finish all!";
}

let gen = times();

let gen1 = gen.next();
gen1.value.then(function(data){
    console.log(data+" one");

    let gen2 = gen.next();
    gen2.value.then(function(data){
        console.log(data+" two");

        let gen3 = gen.next();
        gen3.value.then(function(data){
            console.log(data+" three");



        })

    })

});

仔細(xì)觀(guān)察可以發(fā)現(xiàn),其實(shí)每一個(gè)value的.then()方法都會(huì)傳入一個(gè)相同的回調(diào)函數(shù),這意味著我們可以使用遞歸來(lái)流程化管理整個(gè)異步流程;

改造一下這個(gè)上邊的代碼;

function timeout(ms) {
  return new Promise((resolve, reject) => {
    setTimeout(resolve, ms, "finish");
  });
}

function* times() {
  yield timeout(2000);
  yield timeout(2000);
  yield timeout(2000);
  return "finish all!";
}


function run(fn){
    let gen = fn();

    function next(){
        console.log("finish");
        let result = gen.next();
        if(result.done) return;
        result.value.then(next);
    }
    next();
}

run(times);

OK,現(xiàn)在我們可以使用run函數(shù),使得生成器函數(shù)times里的異步請(qǐng)求,一步接著一步往下執(zhí)行。

那么,這個(gè)run函數(shù)里邊的next到底是什么呢,它其實(shí)是一個(gè)thunk函數(shù);

thunk函數(shù)

Thunk函數(shù)的誕生是源于一個(gè)編譯器設(shè)計(jì)的問(wèn)題:求值策略,即函數(shù)的參數(shù)到底應(yīng)該何時(shí)求值。

看下邊的代碼,請(qǐng)思考什么時(shí)候進(jìn)行求值:

var x = 1;
function f(m) {
    return m * 2;
}
f(x + 5);

試問(wèn):x+5這個(gè)表達(dá)式應(yīng)該什么時(shí)候求值

傳值調(diào)用(call by value),即在進(jìn)入函數(shù)體之間,先計(jì)算x+5的值,再將這個(gè)值(6)傳入函數(shù)f,例如c語(yǔ)言,這種做法的好處是實(shí)現(xiàn)比較簡(jiǎn)單,但是有可能會(huì)造成性能損失。

傳名調(diào)用(call by name),即直接將表達(dá)式(x+5)傳入函數(shù)體,只在用到它的時(shí)候求值。

OK,thunk函數(shù)究竟是什么:

編譯器的傳名調(diào)用實(shí)現(xiàn),往往就是將參數(shù)放到一個(gè)臨時(shí)函數(shù)之中,再將這個(gè)臨時(shí)函數(shù)轉(zhuǎn)入函數(shù)體,這個(gè)臨時(shí)函數(shù)就叫做Thunk函數(shù)。

將上邊的代碼進(jìn)行改造:

var thunk = function () {
    return x + 5;
};

function f(thunk) {
    return thunk() * 2;
}

js中的傳名調(diào)用是什么呢,與真正的thunk有什么區(qū)別呢?

JavaScript 語(yǔ)言是傳值調(diào)用,它的 Thunk 函數(shù)含義有所不同。在 JavaScript 語(yǔ)言中,Thunk 函數(shù)替換的不是表達(dá)式,而是多參數(shù)函數(shù),將其替換成一個(gè)只接受回調(diào)函數(shù)作為參數(shù)的單參數(shù)函數(shù)。

網(wǎng)上對(duì)于thunk的演示都是使用的fs模塊的readFile方法來(lái)進(jìn)行演示

// 正常版本的readFile(多參數(shù)版本)
fs.readFile(fileName, callback);

// Thunk版本的readFile(單參數(shù)版本)
var Thunk = function (fileName) {
  return function (callback) {
    return fs.readFile(fileName, callback);
  };
};

var readFileThunk = Thunk(fileName);
readFileThunk(callback);

其實(shí),任何函數(shù),只要參數(shù)有回調(diào)函數(shù),就能寫(xiě)成Thunk函數(shù)的形式。下面是一個(gè)簡(jiǎn)單的Thunk函數(shù)轉(zhuǎn)換器。

讓我們用setTimeout來(lái)進(jìn)行一次演示:

//正常版本的setTimeout;
setTimeout(function(data){
    console.log(data);
},1000,"finish");

//thunk版本的setTimeout
let thunk = function(time){
    return function(callback){
        return setTimeout(callback,time,"finish");
    }
}
let setTimeoutChunk = thunk(1000);
setTimeoutChunk(function(data){
    console.log(data);
});

現(xiàn)在回頭看一看用Generator函數(shù)封裝異步請(qǐng)求這一節(jié)中最后一個(gè)實(shí)例中,我們封裝的timeout函數(shù),他其實(shí)就是一個(gè)thunk函數(shù),我在那一節(jié)中沒(méi)有給大家說(shuō)明這一條:

yield命令后面的必須是 Thunk 函數(shù)。

為什么Generator里面必須使用thunk函數(shù)呢,因?yàn)槲覀冃枰_保傳入的值只有一個(gè),利用其回調(diào)函數(shù),來(lái)進(jìn)行遞歸自動(dòng)控制Generator函數(shù)的流程,接收和交還程序的執(zhí)行權(quán);

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/94660.html

相關(guān)文章

  • 淺談JavaScript中的事件循環(huán)機(jī)制

    摘要:事件循環(huán)背景是一門(mén)單線(xiàn)程非阻塞的腳本語(yǔ)言,單線(xiàn)程意味著,代碼在執(zhí)行的任何時(shí)候,都只有一個(gè)主線(xiàn)程來(lái)處理所有的任務(wù)。在意識(shí)到該問(wèn)題之際,新特性中的可以讓成為一門(mén)多線(xiàn)程語(yǔ)言,但實(shí)際開(kāi)發(fā)中使用存在著諸多限制。這個(gè)地方被稱(chēng)為執(zhí)行棧。 事件循環(huán)(Event Loop) 背景 JavaScript是一門(mén)單線(xiàn)程非阻塞的腳本語(yǔ)言,單線(xiàn)程意味著,JavaScript代碼在執(zhí)行的任何時(shí)候,都只有一個(gè)主線(xiàn)程來(lái)...

    Pluser 評(píng)論0 收藏0
  • [前端工坊]淺談Web編程中的異步調(diào)用的發(fā)展演變

    摘要:三即生成器,它是生成器函數(shù)返回的一個(gè)對(duì)象,是中提供的一種異步編程解決方案而生成器函數(shù)有兩個(gè)特征,一是函數(shù)名前帶星號(hào),二是內(nèi)部執(zhí)行語(yǔ)句前有關(guān)鍵字調(diào)用一個(gè)生成器函數(shù)并不會(huì)馬上執(zhí)行它里面的語(yǔ)句,而是返回一個(gè)這個(gè)生成器的迭代器對(duì)象。 文章來(lái)自微信公眾號(hào):前端工坊(fe_workshop),不定期更新有趣、好玩的前端相關(guān)原創(chuàng)技術(shù)文章。 如果喜歡,請(qǐng)關(guān)注公眾號(hào):前端工坊版權(quán)歸微信公眾號(hào)所有,轉(zhuǎn)載請(qǐng)...

    qpwoeiru96 評(píng)論0 收藏0
  • 最后一次搞懂 Event Loop

    摘要:由于是單線(xiàn)程的,這些方法就會(huì)按順序被排列在一個(gè)單獨(dú)的地方,這個(gè)地方就是所謂執(zhí)行棧。事件隊(duì)列每次僅執(zhí)行一個(gè)任務(wù),在該任務(wù)執(zhí)行完畢之后,再執(zhí)行下一個(gè)任務(wù)。 Event Loop 是 JavaScript 異步編程的核心思想,也是前端進(jìn)階必須跨越的一關(guān)。同時(shí),它又是面試的必考點(diǎn),特別是在 Promise 出現(xiàn)之后,各種各樣的面試題層出不窮,花樣百出。這篇文章從現(xiàn)實(shí)生活中的例子入手,讓你徹底理解 E...

    gself 評(píng)論0 收藏0
  • 淺談前端中的錯(cuò)誤處理

    摘要:如何避免內(nèi)存泄露內(nèi)存泄漏很常見(jiàn),特別是前端去寫(xiě)后端程序,閉包運(yùn)用不當(dāng),循環(huán)引用等都會(huì)導(dǎo)致內(nèi)存泄漏。有的時(shí)候很難避免一些可能產(chǎn)生內(nèi)存泄漏的問(wèn)題,可以利用每次調(diào)用都在一個(gè)沙箱環(huán)境下調(diào)用,用完回收調(diào)。 某一天用戶(hù)反饋打開(kāi)的頁(yè)面白屏幕,怎么定位到產(chǎn)生錯(cuò)誤的原因呢?日常某次發(fā)布怎么確定發(fā)布會(huì)沒(méi)有引入bug呢?此時(shí)捕獲到代碼運(yùn)行的bug并上報(bào)是多么的重要。 既然捕獲錯(cuò)誤并上報(bào)是日常開(kāi)發(fā)中不可缺少的...

    ShowerSun 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

Magicer

|高級(jí)講師

TA的文章

閱讀更多
最新活動(dòng)
閱讀需要支付1元查看
<