Javascript 異步編程方法匯總

  1. 回調(diào)函數(shù)
    假定有兩個函數(shù)f1和f2,后者等待前者的執(zhí)行結(jié)果。
    f1(); f2();
    如果f1是一個很耗時(shí)的任務(wù),可以考慮改寫為f1,把f2寫為f1的回調(diào)函數(shù)。
    function f1(callback){
       setTimeout(function(){
            //f1代碼
            callback();
        },1000);
    }
    
    執(zhí)行的時(shí)候就變成了f1(f2); 采用這種方法把同步變成異步,f1不會堵塞程序運(yùn)行,相當(dāng)于先執(zhí)行程序的主要邏輯,將耗時(shí)都部分推遲執(zhí)行。
    回調(diào)函數(shù)優(yōu)點(diǎn):簡單、容易理解和部署;缺點(diǎn):不利于代碼的閱讀和維護(hù)。
    一個同步(阻塞)中使用回調(diào)的例子,目的是在func1代碼執(zhí)行完成后執(zhí)行func2代碼。
    var func1 = function(callback){
           // do something 
          (callback && typeof(callback) === "function") && callback();
    }
    func1(func2);
    var func2 = function(){}
    

更多回調(diào)函數(shù)請參考:

  1. 事件監(jiān)聽
    任務(wù)的執(zhí)行不取決于代碼的順序,而取決于某件事件是否發(fā)生。
    為f1綁定一個事件:f1.on('done', f2); 當(dāng)f1發(fā)生done事件,就執(zhí)行f2。然后對f1進(jìn)行改寫為
    function f1(){
    setTimeout(function*(){
         // f1的任務(wù)代碼
         f1.trigger('done');
        },1000);
    }
    
    優(yōu)點(diǎn):比較容易理解,綁定多個事件,每個事件可以指定多個回調(diào)函數(shù),去耦合,實(shí)現(xiàn)模塊化。
  2. 發(fā)布/訂閱
    例如:jquery中一個插件:Tiny Pub/Sub
    1. f2向信號中心"jquery訂閱"done信號: jquery.subscribe("done", f2);
    2. f1函數(shù)如下:
      function f1(){
          setTimeout(function(){
                // f1的執(zhí)行代碼
                 jquey.publish('done');
          }, 1000);
       }
      
      jquey.publish('done');就是f1執(zhí)行完畢之后,向信號中心發(fā)送done信號,從而引發(fā)f2的執(zhí)行。f2執(zhí)行后,也可以取消訂閱jquery.unsubscribe("done", f2);
  3. Promises對象:為異步編程提供統(tǒng)一接口。
    思想:每一個異步任務(wù)返回一個Promise對象,該對象有一個then方法,允許指向回調(diào)函數(shù)。
    Promise的三種狀態(tài):
    • Pending:Promise對象實(shí)例創(chuàng)建時(shí)候的初始狀態(tài)。
    • Fulfilled:成功的狀態(tài)
    • Rejected:失敗的狀態(tài)
      Promise ---> resolved ---> then(回調(diào)callback) 或者 ---> rejected ---> catch(回調(diào)callback)。Promise一旦從等待狀態(tài)變成其他狀態(tài)就永遠(yuǎn)不能改變狀態(tài)了。
       const instance = new Promise((resolve, reject) => {
         // 一些異步操作
         if(/*異步操作成功*/) {
             resolve(value);
         } else {
             reject(error);
             }
         }
       })
       instance.then(value => {
           // do something...
        }, error => {
             // do something...
        })
      
      構(gòu)造函數(shù)內(nèi)部的代碼立刻執(zhí)行。
      then 方法會返回一個新的 Promise 實(shí)例,可以分兩種情況來看:
      • 指定返回值是新的 Promise 對象,如return new Promise(...),這種情況沒啥好說的,由于返回的是 Promise,后面顯然可以繼續(xù)調(diào)用then方法。
      • 返回值不是Promise, 如:return 1 這種情況還是會返回一個 Promise,并且這個Promise 立即執(zhí)行回調(diào) resolve(1)。所以仍然可以鏈?zhǔn)秸{(diào)用then方法。(注:如果沒有指定return語句,相當(dāng)于返回了undefined)。
      1. 示例一
      function sayHi(name) {
         return new Promise((resolve, reject) => {
               setTimeout(() => {
                   resolve(name);
                }, 2000)
          })
      }
      sayHi('張三')
         .then(name => {
               console.log(`你好, ${name}`);
               return sayHi('李四');    // 最終 resolved 函數(shù)中的參數(shù)將作為值傳遞給下一個then
            })
           // name 是上一個then傳遞出來的參數(shù)
         .then(name => {                
             console.log(`你好, ${name}`);
             return sayHi('王二麻子');
           })
         .then(name => {
             console.log(`你好, ${name}`);
         })
       // 你好, 張三
       // 你好, 李四
       // 你好, 王二麻子
      
      1. 示例二
       read('./file-01.txt', 'utf8')
          .then(data => {
            // 因?yàn)?read 方法會返回一個 promise ,返回read執(zhí)行相當(dāng)于返回一個 promise
            // 會將這個 promise 的執(zhí)行成功的結(jié)果傳遞給下一次 then 的 resolve 
                return read(data, 'utf8');
            }, err => {
                console.log(err);
          })
          .then(data => {
            // 如果返回一個普通的值 ,會將這個普通值傳遞倒下一次 then 的成功的參數(shù)
              return [data];
           }, err => {
             console.log(err);
           })
          .then(data => {
            // 返回的是一個普通值
             console.log(data);
             // 沒有寫 return ,相當(dāng)于返回一個 undefined ,下一個then的成功值則為 undefined
              }, err => {
              console.log(err);
            })
          .then(data => {
          // 上一個 then 沒有返回值,默認(rèn)值為 undefined
          // undefined 也算成功
            console.log(data);
            // 拋出錯誤,將傳給下一個 then 的 reject 
              throw new Error('xxx');
          }, err => {
              console.log(err);
        })
         .then(null, err => {
            // 如果上一個 then 拋出錯誤,最近的 reject 會執(zhí)行
            // reject 執(zhí)行后默認(rèn)下一個 then 會接收 undefined 
          console.log(err);
        })
        .then(data => {
            // 上一個 then 中失敗沒有返回值,默認(rèn)為 undefined 
            console.log('resolve');        
          }, err => {
            console.log('reject');
        });
      
      1. 示例三:使用catch代替err
       read('./file-01.txt', 'utf8')
          .then(data => {
              return read(data, 'utf8');
          })
        .then(data => {
            return [data];
        })
        .then(data => {
            console.log(data);
        })
        .then(data => {
          console.log(data);
            // 拋出錯誤后,找到最近的接收錯誤方法
            // 如果所有的 then 都沒有 reject 方法,則找最后一個 catch
            throw new Error('xxx');
          })
        .then(null)
        .then(data => {
            console.log('resolve');        
          })
        .catch(err => {
            console.log(err);
        });
      
  4. async/await
    • async/await是基于Promise實(shí)現(xiàn)的,不能用于普通的回調(diào)函數(shù)。
    • 和Promise一樣,是非阻塞的。
    • 使異步代碼看起來像是同步代碼。
    • 一個函數(shù)如果加上async,那么該函數(shù)就會返回一個Promise,(如果指定的返回值不是Promise對象,也返回一個Promise,只不過立即 resolve ,處理方式同 then 方法,因此 async 函數(shù)通過 return 返回的值,會成為 then 方法中回調(diào)函數(shù)的參數(shù)。單獨(dú)一個 async 函數(shù),其實(shí)與Promise執(zhí)行的功能是一樣的。
    • await 就是異步等待,它等待的是一個Promise,因此 await 后面應(yīng)該寫一個Promise對象,如果不是Promise對象,那么會被轉(zhuǎn)成一個立即 resolve 的Promise
    1. 實(shí)例一
        let fs = require('fs')
        function read(file) {
        return new Promise(function(resolve, reject) {
            fs.readFile(file, 'utf8', function(err, data) {
            if (err) reject(err)
            resolve(data)
            })
        })
        }
        async function readResult(params) {
        try {
            let p1 = await read(params, 'utf8')
            //await后面跟的是一個Promise實(shí)例,
            //await返回的結(jié)果直接return的結(jié)果,不需要進(jìn)行新的then操作。
            let p2 = await read(p1, 'utf8')
            let p3 = await read(p2, 'utf8')
            console.log('p1', p1)
            console.log('p2', p2)
            console.log('p3', p3)
            return p3
        } catch (error) {
            console.log(error)
        }
        }
        readResult('1.txt').then( // async函數(shù)返回的也是個promise
        data => {
            console.log(data)
        },
        err => console.log(err)
        )
        // p1 2.txt
        // p2 3.txt
        // p3 結(jié)束
        // 結(jié)束
      
    2. 示例二
    function readAll() {
       read1()
       read2()//這個函數(shù)同步執(zhí)行
      }
    async function read1() {
       let r = await read('1.txt','utf8')
       console.log(r)
     }
    async function read2() {
       let r = await read('2.txt','utf8')
       console.log(r)
     }
    readAll() // 2.txt 3.txt
    
    1. 示例三
    async function func() {
        try {
            const num1 = await 200;
            console.log(`num1 is ${num1}`);
            const num2 = await Promise.reject('num2 is wrong!');
            console.log(`num2 is ${num2}`);
            const num3 = await num2 + 100;
            console.log(`num3 is ${num3}`);
        } catch (error) {
            console.log(error);
        }
     }
    
    func();
    // num1 is 200
    // 出錯了
    // num2 is wrong!
    

參考:
- Javascript異步編程的4種方法
- 異步方法的發(fā)展流程
- JS 異步編程六種方案

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Promise 對象 Promise 的含義 Promise 是異步編程的一種解決方案,比傳統(tǒng)的解決方案——回調(diào)函...
    neromous閱讀 8,832評論 1 56
  • 弄懂js異步 講異步之前,我們必須掌握一個基礎(chǔ)知識-event-loop。 我們知道JavaScript的一大特點(diǎn)...
    DCbryant閱讀 2,879評論 0 5
  • 前言 本文旨在簡單講解一下javascript中的Promise對象的概念,特性與簡單的使用方法。并在文末會附上一...
    _暮雨清秋_閱讀 2,312評論 0 3
  • 在ES6當(dāng)中添加了很多新的API其中很值得一提的當(dāng)然少不了Promise,因?yàn)镻romise的出現(xiàn),很輕松的就給開...
    嘿_那個誰閱讀 3,746評論 2 3
  • 原文連接:https://blog.csdn.net/sinat_17775997/article/details...
    小豆soybean閱讀 4,363評論 0 7

友情鏈接更多精彩內(nèi)容