国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

Javascript中的異步編程

wemall / 968人閱讀

摘要:接下來,我們一起來看看中的異步編程,具體有哪幾種。實(shí)現(xiàn)異步編程的方法一回調(diào)函數(shù)上面不止一次提到了回調(diào)函數(shù)。它是異步編程中,最基本的方法。四對(duì)象接下來,我們聊聊與相關(guān)的異步編程方法,對(duì)象。

前言

最近,小伙伴S 問了我一段代碼:

const funB = (value) => {
    console.log("funB "+ value);
};

const funA = (callback) => {
    ...
    setTimeout(() => {
        typeof callback === "function" && callback("is_ok!");
    }, 1000);
}

funA(funB);

他不太理解這段代碼中,funB 函數(shù)作為 funA 函數(shù)的參數(shù)這樣的寫法。從語義上看,callback 的意思是回調(diào),那么是說 funB 是 funA 的回調(diào)嘛?

我給他解釋說,funB 函數(shù)的確是 funA 函數(shù)的回調(diào),它會(huì)等待 funA 中前面的語句都執(zhí)行完,再去執(zhí)行。這是一種異步編程的寫法。

小伙伴S 還是有點(diǎn)不太理解:異步編程是什么?除了回調(diào)函數(shù)之外,異步編程還有哪些?

別急,讓我們先從概念入手,再逐個(gè)理解異步編程中的方法,看看它的前世今生。

什么是異步?

所謂"異步"(Asynchronous),可以理解為一種不連續(xù)的執(zhí)行。簡單地說,就是把一個(gè)任務(wù)分成兩段,先執(zhí)行第一段,然后轉(zhuǎn)而執(zhí)行其他任務(wù),等接到通知了,再回過頭執(zhí)行第二段。

我們都知道,JavaScript是單線程的。而異步,對(duì)于JavaScript的重要性,則體現(xiàn)在非阻塞這一點(diǎn)上。一些常見的異步有:

onclick 在其事件觸發(fā)的時(shí)候,回調(diào)會(huì)立即添加到任務(wù)隊(duì)列中。

setTimeout 只有當(dāng)時(shí)間到達(dá)的時(shí)候,才會(huì)將回調(diào)添加到任務(wù)隊(duì)列中。

ajax 在網(wǎng)絡(luò)請(qǐng)求完成并返回之后,才將回調(diào)添加到任務(wù)隊(duì)列中。

接下來,我們一起來看看Javascript中的異步編程,具體有哪幾種。

實(shí)現(xiàn)異步編程的方法 一、回調(diào)函數(shù)

上面不止一次提到了回調(diào)函數(shù)。它從概念上說很簡單,就是把任務(wù)的第二段多帶帶寫在一個(gè)函數(shù)里面,等到重新執(zhí)行這個(gè)任務(wù)的時(shí)候,就直接調(diào)用這個(gè)函數(shù)。它是異步編程中,最基本的方法。

舉個(gè)例子,假定有兩個(gè)函數(shù) f1 和 f2,后者等待前者的執(zhí)行結(jié)果。順序執(zhí)行的話,可以這樣寫:

f1();
f2();

但是,如果 f1 是一個(gè)很耗時(shí)的任務(wù),該怎么辦?

改寫一下 f1,把 f2 寫成 f1 的回調(diào)函數(shù):

const f1 = (callback) => {
    setTimeout(() => {
        typeof callback === "function" && callback();
    }, 1000);
}
f1(f2);
二、事件監(jiān)聽

onclick 的寫法,在異步編程中,稱為事件監(jiān)聽。它的思路是:如果任務(wù)的執(zhí)行不取決于代碼的順序,而取決于某個(gè)事件是否發(fā)生,也就事件驅(qū)動(dòng)模式。

還是 f1 和 f2 的例子,為了簡化代碼,這里采用jQuery的寫法:

// 為f1綁定一個(gè)事件,當(dāng)f1發(fā)生done事件,就執(zhí)行f2
f1.on("done", f2);

// 改寫f1
function f1(){
    setTimeout(() => {
        // f1的任務(wù)代碼,執(zhí)行完成后,立即觸發(fā)done事件
        f1.trigger("done");
    }, 1000);
}

它的優(yōu)點(diǎn)是:比較容易理解,耦合度降低了。可以綁定多個(gè)事件,而且每個(gè)事件還能指定多個(gè)回調(diào)函數(shù)。

缺點(diǎn)是:整個(gè)程序都會(huì)變?yōu)橛墒录眚?qū)動(dòng),流程會(huì)變得很不清晰。

三、發(fā)布/訂閱

這是一種為了處理一對(duì)多的業(yè)務(wù)場景而誕生的設(shè)計(jì)模式,它也是一種異步編程的方法。vue中MVVM的實(shí)現(xiàn),就有它的功勞。

關(guān)于概念,我們可以這樣理解,假定存在一個(gè)"信號(hào)中心",某個(gè)任務(wù)執(zhí)行完成,就向信號(hào)中心"發(fā)布"(publish)一個(gè)信號(hào),其他任務(wù)可以向信號(hào)中心"訂閱"(subscribe)這個(gè)信號(hào),從而知道什么時(shí)候自己可以開始執(zhí)行。這就叫做"發(fā)布/訂閱模式"(publish-subscribe pattern),又稱"觀察者模式"(observer pattern)。

下面的例子,采用的是 Morgan Roderick 的 PubSubJS ,這是一個(gè)無依賴的JavaScript插件:

import PubSub from "pubsub-js";

// f2向 "PubSub" 訂閱信號(hào) "done"
PubSub.subscribe("done", f2);

const f1 = () => {
    setTimeout(() => {
        // f1執(zhí)行完成后,向 "PubSub" 發(fā)布信號(hào) "done",從而執(zhí)行 f2
        PubSub.publish("done");
    }, 1000);
};
f1();

// f2 完成執(zhí)行后,也可以取消訂閱
PubSub.unsubscribe("done", f2);

這種模式有點(diǎn)類似于“事件監(jiān)聽”,但是明顯優(yōu)于后者。因?yàn)椋覀兛梢酝ㄟ^查看“消息中心”,了解存在多少信號(hào)、每個(gè)信號(hào)有多少訂閱者,從而監(jiān)控程序的運(yùn)行。

四、Promise對(duì)象

接下來,我們聊聊與ajax相關(guān)的異步編程方法,Promise對(duì)象。

Promise 是由 CommonJS 提出的一種規(guī)范,它是為了解決回調(diào)函數(shù)嵌套,也就是回調(diào)地獄的問題。它不是新的語法功能,而是一種新的寫法,允許將回調(diào)函數(shù)的橫向加載,改成縱向加載。它的思想是,每一個(gè)異步任務(wù)返回一個(gè)Promise對(duì)象,該對(duì)象有一個(gè)then方法,允許指定回調(diào)函數(shù)。

繼續(xù)改寫 f1 和 f2:

const f1 = () => {
    return new Promise((resolve, reject) => {
        let timeOut = Math.random() * 2;
        setTimeout(() => {
            if (timeOut < 1) {
                resolve("200 OK");
            } else {
                reject("timeout in " + timeOut + " seconds.");
            }
        }, 1000);
    });  
};

const f2 = () => {
    console.log("start f2");  
};

f1().then((result) => {
    console.log(result);
    f2();
}).catch((reason) => {
    ...
);

例子中,用隨機(jī)數(shù)模擬了請(qǐng)求的超時(shí)。當(dāng) f1 返回 Promise 的 resolve 時(shí),執(zhí)行 f2。

Promise的優(yōu)點(diǎn)是:回調(diào)函數(shù)變成了鏈?zhǔn)降膶懛ǎ绦虻牧鞒炭梢钥吹煤芮宄_€有就是,如果一個(gè)任務(wù)已經(jīng)完成,再添加回調(diào)函數(shù),該回調(diào)函數(shù)會(huì)立即執(zhí)行。所以,你不用擔(dān)心是否錯(cuò)過了某個(gè)狀態(tài)。

缺點(diǎn)就是:編寫和理解,都相對(duì)比較難。

五、Generator

generator(生成器)是 ES6 標(biāo)準(zhǔn)引入的數(shù)據(jù)類型。它最大特點(diǎn),就是可以交出函數(shù)的執(zhí)行權(quán)(即暫停執(zhí)行),是協(xié)程在 ES6 中的實(shí)現(xiàn)。

看上去它像一個(gè)函數(shù),定義如下:

function* gen(x) {
  var y = yield x + 2;
  return y;
}

它不同于普通函數(shù),函數(shù)名之前要加星號(hào)(*),是可以暫停執(zhí)行的。

整個(gè) Generator 函數(shù)就是一個(gè)封裝的異步任務(wù),或者說是異步任務(wù)的容器。用 yield 語句注明異步操作需要暫停的地方。

我們來看一下 Generator 函數(shù)執(zhí)行的過程:

var g = gen(1);

// { value: 3, done: false }
g.next();
// { value: undefined, done: true }
g.next();

上面代碼中,調(diào)用 Generator 函數(shù),會(huì)返回一個(gè)內(nèi)部指針(即遍歷器 )g 。這是 Generator 函數(shù)不同于普通函數(shù)的另一個(gè)地方,即執(zhí)行它不會(huì)返回結(jié)果,返回的是指針對(duì)象。調(diào)用指針 g 的 next 方法,會(huì)移動(dòng)內(nèi)部指針(即執(zhí)行異步任務(wù)的第一段),指向第一個(gè)遇到的 yield 語句,上例是執(zhí)行到 x + 2 為止。

換言之,next 方法的作用是分階段執(zhí)行 Generator 函數(shù)。每次調(diào)用 next 方法,會(huì)返回一個(gè)對(duì)象,表示當(dāng)前階段的信息( value 屬性和 done 屬性)。value 屬性是 yield 語句后面表達(dá)式的值,表示當(dāng)前階段的值;done 屬性是一個(gè)布爾值,表示 Generator 函數(shù)是否執(zhí)行完畢,即是否還有下一個(gè)階段。

六、async/await

這是 ES8 中提出的一種更優(yōu)雅的異步解決方案,靈感來自于 C# 語言。具體可前往 細(xì)說 async/await 相較于 Promise 的優(yōu)勢 ,深入理解其原理及特性。

來看個(gè)例子,要實(shí)現(xiàn)一個(gè)暫停功能,輸入 N 毫秒,則停頓 N 毫秒后才繼續(xù)往下執(zhí)行。

const sleep = (time) => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve();
        }, time);
    })
};

const start = async () => {
    console.log("start");
    // 在這里使用起來就像同步代碼那樣直觀
    await sleep(1000);
    console.log("end");
};

start();

控制臺(tái)先輸出 start,稍等 1 秒后,輸出結(jié)果 ok,最后輸出 end。

解析一下上述代碼:

async 表示這是一個(gè)async函數(shù),await 只能用在這個(gè)函數(shù)里面。

await 表示在這里等待 promise 返回了結(jié)果,再繼續(xù)執(zhí)行。

使用起來,就像寫同步代碼一樣地優(yōu)雅。

總結(jié)

JavaScript的異步編寫方式,從 回調(diào)函數(shù) 到 async/await,感覺在寫法上,每次都有進(jìn)步,其本質(zhì)就是一次次對(duì)語言層抽象的優(yōu)化。以至于現(xiàn)在,我們可以像同步一樣地,去處理異步。

換句話說就是:異步編程的最高境界,就是根本不用關(guān)心它是不是異步

PS:歡迎關(guān)注我的公眾號(hào) “超哥前端小棧”,交流更多的想法與技術(shù)。

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

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/101014.html

相關(guān)文章

  • ES6-7

    摘要:的翻譯文檔由的維護(hù)很多人說,阮老師已經(jīng)有一本關(guān)于的書了入門,覺得看看這本書就足夠了。前端的異步解決方案之和異步編程模式在前端開發(fā)過程中,顯得越來越重要。為了讓編程更美好,我們就需要引入來降低異步編程的復(fù)雜性。 JavaScript Promise 迷你書(中文版) 超詳細(xì)介紹promise的gitbook,看完再不會(huì)promise...... 本書的目的是以目前還在制定中的ECMASc...

    mudiyouyou 評(píng)論0 收藏0
  • JavaScript 異步

    摘要:從最開始的到封裝后的都在試圖解決異步編程過程中的問題。為了讓編程更美好,我們就需要引入來降低異步編程的復(fù)雜性。寫一個(gè)符合規(guī)范并可配合使用的寫一個(gè)符合規(guī)范并可配合使用的理解的工作原理采用回調(diào)函數(shù)來處理異步編程。 JavaScript怎么使用循環(huán)代替(異步)遞歸 問題描述 在開發(fā)過程中,遇到一個(gè)需求:在系統(tǒng)初始化時(shí)通過http獲取一個(gè)第三方服務(wù)器端的列表,第三方服務(wù)器提供了一個(gè)接口,可通過...

    tuniutech 評(píng)論0 收藏0
  • 夯實(shí)基礎(chǔ)-JavaScript異步編程

    摘要:調(diào)用棧被清空,消息隊(duì)列中并無任務(wù),線程停止,事件循環(huán)結(jié)束。不確定的時(shí)間點(diǎn)請(qǐng)求返回,將設(shè)定好的回調(diào)函數(shù)放入消息隊(duì)列。調(diào)用棧執(zhí)行完畢執(zhí)行消息隊(duì)列任務(wù)。請(qǐng)求并發(fā)回調(diào)函數(shù)執(zhí)行順序無法確定。 異步編程 JavaScript中異步編程問題可以說是基礎(chǔ)中的重點(diǎn),也是比較難理解的地方。首先要弄懂的是什么叫異步? 我們的代碼在執(zhí)行的時(shí)候是從上到下按順序執(zhí)行,一段代碼執(zhí)行了之后才會(huì)執(zhí)行下一段代碼,這種方式...

    shadowbook 評(píng)論0 收藏0
  • JavaScript 異步編程

    摘要:下面我將介紹的基本用法以及如何在異步編程中使用它們。在沒有發(fā)布之前,作為異步編程主力軍的回調(diào)函數(shù)一直被人詬病,其原因有太多比如回調(diào)地獄代碼執(zhí)行順序難以追蹤后期因代碼變得十分復(fù)雜導(dǎo)致無法維護(hù)和更新等,而的出現(xiàn)在很大程度上改變了之前的窘境。 前言 自己著手準(zhǔn)備寫這篇文章的初衷是覺得如果想要更深入的理解 JS,異步編程則是必須要跨過的一道坎。由于這里面涉及到的東西很多也很廣,在初學(xué) JS 的...

    lordharrd 評(píng)論0 收藏0
  • JS筆記

    摘要:從最開始的到封裝后的都在試圖解決異步編程過程中的問題。為了讓編程更美好,我們就需要引入來降低異步編程的復(fù)雜性。異步編程入門的全稱是前端經(jīng)典面試題從輸入到頁面加載發(fā)生了什么這是一篇開發(fā)的科普類文章,涉及到優(yōu)化等多個(gè)方面。 TypeScript 入門教程 從 JavaScript 程序員的角度總結(jié)思考,循序漸進(jìn)的理解 TypeScript。 網(wǎng)絡(luò)基礎(chǔ)知識(shí)之 HTTP 協(xié)議 詳細(xì)介紹 HTT...

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

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

0條評(píng)論

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