日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

切面是异步还是同步操作‘_细说JS异步发展历程

發(fā)布時間:2024/9/27 javascript 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 切面是异步还是同步操作‘_细说JS异步发展历程 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

知其然知其所以然,首先了解三個概念:

1.什么是同步?

所謂同步,就是在發(fā)出一個"調用"時,在沒有得到結果之前,該“調用”就不返回。但是一旦調用返回,就得到返回值了。換句話說,就是由“調用者”主動等待這個“調用”的結果。此調用執(zhí)行完之前,阻塞之后的代碼執(zhí)行。

2.什么是異步?

"調用"在發(fā)出之后,這個調用就直接返回了,沒有返回結果。換句話說,當一個異步過程調用發(fā)出后,調用者不會立刻得到結果。而是在"調用"發(fā)出后,"被調用者"通過狀態(tài)、通知來通知調用者,或通過回調函數處理這個調用。異步調用發(fā)出后,不影響后面代碼的執(zhí)行。

3.JavaScript 中為什么需要異步?

首先我們知道JavaScript是單線程的(即使新增了webworker,但是本質上JS還是單線程)。同步代碼意味著什么呢?意味著有可能會阻塞,當我們有一個任務需要時間較長時,如果使用同步方式,那么就會阻塞之后的代碼執(zhí)行。而異步則不會,我們不會等待異步代碼的執(zhí)行,繼續(xù)執(zhí)行異步任務之后的代碼。

概念了解完了,我們就要進入今天的正題了。首先大家思考一下:平時在工作中,主要使用了哪些異步解決方案,這些異步方案有什么優(yōu)缺點?

異步最早的解決方案是回調函數,如事件的回調,setInterval/setTimeout中的回調。但是回調函數有一個很常見的問題,就是回調地獄的問題(稍后會舉例說明);

為了解決回調地獄的問題,社區(qū)提出了Promise解決方案,ES6將其寫進了語言標準。Promise一定程度上解決了回調地獄的問題,但是Promise也存在一些問題,如錯誤不能被try catch,而且使用Promise的鏈式調用,其實并沒有從根本上解決回調地獄的問題,只是換了一種寫法。

ES6中引入 Generator 函數,Generator是一種異步編程解決方案,Generator 函數是協(xié)程在 ES6 的實現,最大特點就是可以交出函數的執(zhí)行權,Generator 函數可以看出是異步任務的容器,需要暫停的地方,都用yield語句注明。但是 Generator 使用起來較為復雜。

ES7又提出了新的異步解決方案:async/await,async是 Generator 函數的語法糖,async/await 使得異步代碼看起來像同步代碼,異步編程發(fā)展的目標就是讓異步邏輯的代碼看起來像同步一樣。

回調函數 ---> Promise ---> Generator ---> async/await.

1.callback

//node讀取文件

fs.readFile(xxx, 'utf-8', function(err, data) {

//code

});

回調函數的使用場景(包括但不限于):

  • 事件回調

  • Node API

  • setTimeout/setInterval中的回調函數

  • ajax 請求

  • 回調函數的優(yōu)點: 簡單。

    回調函數的缺點:

    異步回調嵌套會導致代碼難以維護,并且不方便統(tǒng)一處理錯誤,不能?trycatch?和 回調地獄(如先讀取A文本內容,再根據A文本內容讀取B再根據B的內容讀取C...)。

    fs.readFile(A, 'utf-8', function(err, data) {

    fs.readFile(B, 'utf-8', function(err, data) {

    fs.readFile(C, 'utf-8', function(err, data) {

    fs.readFile(D, 'utf-8', function(err, data) {

    //....

    });

    });

    });

    });

    2.Promise

    Promise 一定程度上解決了回調地獄的問題,Promise 最早由社區(qū)提出和實現,ES6 將其寫進了語言標準,統(tǒng)一了用法,原生提供了Promise對象。

    那么我們看看Promise是如何解決回調地獄問題的,仍然以上文的readFile 為例(先讀取A文本內容,再根據A文本內容讀取B再根據B的內容讀取C)。

    function read(url) {

    return new Promise((resolve, reject) => {

    fs.readFile(url, 'utf8', (err, data) => {

    if(err) reject(err);

    resolve(data);

    });

    });

    }

    read(A).then(data => {

    return read(B);

    }).then(data => {

    return read(C);

    }).then(data => {

    return read(D);

    }).catch(reason => {

    console.log(reason);

    });

    Promise 的優(yōu)點:

  • 一旦狀態(tài)改變,就不會再變,任何時候都可以得到這個結果

  • 可以將異步操作以同步操作的流程表達出來,避免了層層嵌套的回調函數

  • 缺點:

  • 無法取消 Promise

  • 當處于pending狀態(tài)時,無法得知目前進展到哪一個階段

  • 錯誤不能被?trycatch

  • 假設有這樣一個需求:讀取A,B,C三個文件內容,都讀取成功后,再輸出最終的結果。在Promise之前,我們一般可以借助發(fā)布訂閱模式去實現:

    let pubsub = {

    arry: [],

    emit() {

    this.arry.forEach(fn => fn());

    },

    on(fn) {

    this.arry.push(fn);

    }

    }

    let data = [];

    pubsub.on(() => {

    if(data.length === 3) {

    console.log(data);

    }

    });

    fs.readFile(A, 'utf-8', (err, value) => {

    data.push(value);

    pubsub.emit();

    });

    fs.readFile(B, 'utf-8', (err, value) => {

    data.push(value);

    pubsub.emit();

    });

    fs.readFile(C, 'utf-8', (err, value) => {

    data.push(value);

    pubsub.emit();

    });

    Promise給我們提供了?Promise.all?的方法,對于這個需求,我們可以使用?Promise.all?來實現。

    /**

    * 將 fs.readFile 包裝成promise接口

    */

    function read(url) {

    return new Promise((resolve, reject) => {

    fs.readFile(url, 'utf8', (err, data) => {

    if(err) reject(err);

    resolve(data);

    });

    });

    }

    /**

    * 使用 Promise

    *

    * 通過 Promise.all 可以實現多個異步并行執(zhí)行,同一時刻獲取最終結果的問題

    */

    Promise.all([

    read(A),

    read(B),

    read(C)

    ]).then(data => {

    console.log(data);

    }).catch(err => console.log(err));

    可執(zhí)行代碼可戳: https://github.com/YvetteLau/Blog/blob/master/JS/Async/index.js

    3.Generator

    Generator 函數是 ES6 提供的一種異步編程解決方案,整個 Generator 函數就是一個封裝的異步任務,或者說是異步任務的容器。異步操作需要暫停的地方,都用 yield 語句注明。

    Generator 函數一般配合 yield 或 Promise 使用。Generator函數返回的是迭代器。對生成器和迭代器不了解的同學,請自行補習下基礎。下面我們看一下 Generator 的簡單使用:

    function* gen() {

    let a = yield 111;

    console.log(a);

    let b = yield 222;

    console.log(b);

    let c = yield 333;

    console.log(c);

    let d = yield 444;

    console.log(d);

    }

    let t = gen();

    //next方法可以帶一個參數,該參數就會被當作上一個yield表達式的返回值

    t.next(1); //第一次調用next函數時,傳遞的參數無效

    t.next(2); //a輸出2;

    t.next(3); //b輸出3;

    t.next(4); //c輸出4;

    t.next(5); //d輸出5;

    為了讓大家更好的理解上面代碼是如何執(zhí)行的,我畫了一張圖,分別對應每一次的next方法調用:

    仍然以上文的 readFile (先讀取A文本內容,再根據A文本內容讀取B再根據B的內容讀取C)為例,使用 Generator + co庫來實現:

    const fs = require('fs');

    const co = require('co');

    const bluebird = require('bluebird');

    const readFile = bluebird.promisify(fs.readFile);

    function* read() {

    yield readFile(A, 'utf-8');

    yield readFile(B, 'utf-8');

    yield readFile(C, 'utf-8');

    //....

    }

    co(read()).then(data => {

    //code

    }).catch(err => {

    //code

    });

    Generator的缺點大約不用我說了,除非是找虐,不然一般不會直接使用 Generator 來解決異步的(當然也不排除是因為我不熟練)~~~

    不使用co庫,如何實現?能否自己寫一個最簡的 my_co,有助于理解 async/await 的實現原理 ?請戳: https://github.com/YvetteLau/Blog/blob/master/JS/Async/generator.js

    PS: 如果你還不太了解 Generator/yield,建議閱讀ES6相關文檔。

    4.async/await

    ES7中引入了 async/await 概念。async 其實是一個語法糖,它的實現就是將 Generator函數和自動執(zhí)行器(co),包裝在一個函數中。

    async/await 的優(yōu)點是代碼清晰,不用像 Promise 寫很多 then 鏈,就可以處理回調地獄的問題。并且錯誤可以被try catch。

    仍然以上文的readFile (先讀取A文本內容,再根據A文本內容讀取B再根據B的內容讀取C) 為例,使用 async/await 來實現:

    const fs = require('fs');

    const bluebird = require('bluebird');

    const readFile = bluebird.promisify(fs.readFile);

    async function read() {

    await readFile(A, 'utf-8');

    await readFile(B, 'utf-8');

    await readFile(C, 'utf-8');

    //code

    }

    read().then((data) => {

    //code

    }).catch(err => {

    //code

    });

    使用 async/await 實現此需求:讀取A,B,C三個文件內容,都讀取成功后,再輸出最終的結果。

    function read(url) {

    return new Promise((resolve, reject) => {

    fs.readFile(url, 'utf8', (err, data) => {

    if(err) reject(err);

    resolve(data);

    });

    });

    }

    async function readAsync() {

    let data = await Promise.all([

    read(A),

    read(B),

    read(C)

    ]);

    return data;

    }

    readAsync().then(data => {

    console.log(data);

    });

    所以JS的異步發(fā)展史,可以認為是從 callback -> promise -> generator -> async/await。async/await 使得異步代碼看起來像同步代碼,異步編程發(fā)展的目標就是讓異步邏輯的代碼看起來像同步一樣。

    因本人水平有限,文中內容未必百分百正確,如有不對的地方,請給我留言,謝謝。

    邀請你加入?Step-By-Step?項目

    不積跬步無以至千里。 我是公眾號【前端宇宙】作者劉小夕,我將和大家一起一步一個腳印,向前端專家邁進。Step-By-Step

    每個工作日我會發(fā)布一個前端相關的問題(目的是為了切實掌握相關的知識點),歡迎在 Issue 區(qū)留下你的答案。

    節(jié)假日不會發(fā)布任何問題,希望大家能夠利用節(jié)假日回顧一周所學。每周末我會進行一次匯總(整理出最優(yōu)答案),以便大家回顧。

    參考文章:

    [1]?細說JavaScript異步函數發(fā)展歷程

    [2]?ES6 Promise

    [3]?ES6 Generator

    [4]?ES6 async

    [5]?JavaScript異步編程

    謝謝各位小伙伴愿意花費寶貴的時間閱讀本文,如果本文給了您一點幫助或者是啟發(fā),請不要吝嗇你的贊和Star,您的肯定是我前進的最大動力。https://github.com/YvetteLau/Blog

    關注小姐姐的公眾號,加入交流群。

    總結

    以上是生活随笔為你收集整理的切面是异步还是同步操作‘_细说JS异步发展历程的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。