js 異步處理

一.基礎知識鞏固

  • 同步(Synchronous): 代碼依次向下執行,若是遇到請求獲取其餘的,等待執行完了,以後再執行後面的代碼
  • 異步(Asychronous): 代碼依次向下執行,遇到異步的代碼(事件、Ajax、setTimeout、setInterval、Promise 、Node...無需中斷,,則繼續執行後面的代碼,等到他們請求完畢,在回調裏面去執行他們)
  • js單線程
  • js執行順序,主線程 ==> 異步(ajax,promise,fetch)==> 隊列(setTimeout/setInterval)

二.異步出現狀況

  • setTimeout/setInterval
  • 元素綁定事件
  • Ajax, Fetch

三.異步發展

  • 回調函數(callback) [回調地獄問題,無限回調,維護空難]vue

  • 事件監聽(Listener)node

  • 觀察者模式(發佈訂閱模式) DOM二級綁定多個事件(addEventListener) 原理就是觀察者模式 ,事件池概念出來,jQ多個onios

  • Promise類面試

  • Generator函數ajax

  • Async 函數 (ES2017) 配合Proise使用 [暴爽]axios

四.回調函數 (將要執行的 任務放到一個函數裏面,做爲參數去傳給另外一個函數,另外一個函數執行完畢,去執行這個函數)

  • 簡單,容易理解和 部署
  • 不利於代碼的閱讀,和維護,各部分之間高度耦合,流程會很混亂,並且每個任務只能指定一個回調函數。

eg1:數組

// ==>> callback
   function fn(callback) {
     var x = 1;
   callback && callback(x); //函數fn裏面的變量傳給了他的回調裏面

   }

   fn(function (val) {
       console.log(val);
   });
   
複製代碼

eg2:promise

// ==>> ajax callback
function fn(callback) {
    $.ajax({
        url: '...',
        type: 'get',
        success(data) {
            if (data.result.code === 200) {
                callback && callback(data.result);
            }
        }
    });
}

fn(function (data) {
    console.log(data);
});

複製代碼

eg3:bash

// ==>> 回調地獄
function getData() {
    //1. 獲取token
    $.ajax({
        url: '...',
        success: function (data) {
            if (data.code === 200) {
                //2. 獲取用戶信息
                $.ajax({
                    url: '...',
                    success: function () {
                        if (data.code === 200) {
                            //3.獲取用戶相關的新聞
                            $.ajax({
                                url: '...',
                                success: function () {
                                    if (data.code === 200) {
                                        ...
                                    }
                                }
                            });
                        }
                    }
                });
            }
        }
    });
}
getData();

複製代碼

五.事件監聽(事件驅動模式) 自定義事件問題

  • 任務的執行不取決代碼的順序,而取決於某一個事件是否發生。
  • 監聽函數有:on,bind,listen,addEventListener,attachEvent, observe(vue裏面)
  • 整個程序都要變成事件驅動型,運行流程會變得不清晰。

***綁定事件三種方式

  • 行內綁定click異步

    • 又包了一層,單擊的時候,走的是裏面,裏面去調用你寫的那個函數
  • 給DOM對象綁定方法,方法相同就會有後面覆蓋前面的問題 div1.onclick = function() {};

    • 沒有兼容性問題
    • 由於是屬性值,後面的會覆蓋前面的,同一個事件同一個DOM元素只能綁定一次
  • DOM2二級事件 (addEventListener(eventName,fn, true/false)/attachEvent)

    • 有兼容性問題
    • 能夠綁定多個,出來了一個<<事件池>>的概念,將事件push到裏面,單擊的時候,循環依次執行裏面的函數
    • jQ裏面的on就是封裝了DOM2二級事件
    • 原理是發佈訂閱模式,將全部的事件放到一個事件池裏面,循環依次去執行

待續...

六.Promise對象

  • 是一個類,類是有原型的Promise.prototype 裏面有 then, catch, finally
  • 是類,也有靜態方法 Promise.resolve()/reject()/all()/race()
  • 爲了解決callback回調地獄的,它返回是一個promise實例,因此能夠鏈式調用 (return this) then方法執行完了,返回一個promise對象

三種狀態:(生命週期)

  • pendding 進行中

  • fulfilled 已成功 ==> resolve

  • rejected 已失敗 ==> reject

    pending ==> fulfilled

    pending ==> rejected

***一旦狀態改變,就不會再變,任什麼時候候均可以獲得這個結果, 返回promise實例,鏈式調用

*** Promise的構造函數中代碼是同步執行的,可是then方法是異步執行的,then方法須要等到等到resolve函數執行時才獲得執

*** then返回一個基本或則引用數據類型會自動調用Promise.resolve(val);進行轉爲promise對象,給到下一個then,若是你拋出一個錯誤,這下一個catch會捕獲到

基本語法

let promsie1 = new Promise((resolve, reject) => {
      resolve; 成功
      reject;失敗
  });
  promise instanceof Promise; // ==> true; 
  promise instanceof Object; // ==> true;
  
  // ==>查看類的屬性和方法(原型上,靜態方法,私有屬性)
  console.dir(Promise.prototype);
  console.dir(Promise);
  console.dir(promise1);
  
  //==>返回Promise實例, then裏面能夠寫兩個函數,表明resolve, reject,可是咱們通常捕獲錯誤,不那麼寫,用catch
  promise1.then(data => {
      console.log(data);
  }).catch(err => {
      console.log(err);
  });
複製代碼

1.Promise.resovle();

  • 1.將現有的東西轉成一個promise
  • 2.變成resolve狀態,成功狀態
let promise1 = Promise.resolve(100);
    promise1.then(data => {
       console.log(data); //==> 100; 
    });
    
    等價於下面的
    
    let promsie1 = new Promise(resolve =>{
       resolve(100); 
    });
    promise1.then(data => {
      console.log(data); //==> 100; 
    });
複製代碼
let x = 2;
    let p1 = new Promise((resolve, reject) => {
        if (x >=1) {
            resolve(x);
        } else {
            reject('error')
        }
    });
    p1.then(res => {
        // return res;   // ====>> 返回一個新的Promise實例,進行鏈式調用
        return Promise.resolve(res);
    }).then(res => {
        console.log(res);
    }).then(res => {
        console.log(111);
    });
複製代碼

2.Promise.reject();

  • 1.將現有的東西轉成一個promise
  • 2.變成reject狀態,成功狀態
let promise1 = Promise.reject(100);
    promise1.then(data => {
       console.log(data); //==> 100; 
    });
    
    等價於下面的
    
    let promsie1 = new Promise((resolve, reject) =>{
       reject(100); 
    });
    promise1.then(data => {
      console.log(data);
    }).catch(err => {
          console.log(data); //==> 100; 
    });
複製代碼

3.Promise.all([p1, p2, p3...]);

場景: 當首頁的數據都請求完畢再進行展現出來,就用到了

  • 把promise打包,扔到一個數組裏面,打包完成仍是一個promise對象
  • 返回一個數組,裏面有他們依次執行resolve的值, 用數組結構去接收 let [result1,result] = res;
  • 若是有一個失敗了,則就走到catch裏面了
  • 只有p一、p二、p3的狀態都變成fulfilled,p的狀態纔會變成fulfilled,此時p一、p二、p3的返回值組成一個數組,傳遞給p的回調函數。
  • 只要p一、p二、p3之中有一個被rejected,p的狀態就變成rejected,此時第一個被reject的實例的返回值,會傳遞給p的回調函數catch裏面
eg1:
        let p1 = Promise.resolve(100);
        let p2 = Promise.resolve(200);
        
        let promise2 = Promise.all([p1, p2]);
        promise2.then(data => {
            let [result1, result2] = data;
            console.log(result1, result2); // ==> 100, 200
        }).catch(err => {
            console.log(err);
        });
        
        
    eg2:
        let p1 = Promise.resolve(100);
        let p2 = Promise.reject(200);
        
        let promise2 = Promise.all([p1, p2]);
        promise2.then(data => {
            let [result1, result2] = data;  // ==> 數組解構賦值
            console.log(result1, result2); // ==> 100, 200
        }).catch(err => {
            console.log(err);
        });
        
複製代碼

4.Promise.race([p1, p2, p3...]);

場景: 先來先得

  • 把promise打包,扔到一個數組裏面,打包完成仍是一個promise對象
  • 哪一個率先改變的Promise狀態,則實例的返回值,就傳遞給回調函數。 成功傳個then,失敗傳給catch
let p1 = Promise.reject('1');
    let p2 = Promise.reject('2');
    let p3 = Promise.resolve('3');
    
    let p = Promise.race([p1, p2, p3]).then(res => {
    	console.log(res);
    }).catch(err => {
    	console.log(err); //==> 1
    });
複製代碼

5.Promise.prototype.then()

  • 是原型上的方法
  • resolve的時候去回調then
  • 返回一個新的Promise實例,鏈式寫法,能夠then
let getToken = function() {
    ...
 };
 
 let getUserInfo = function() {
    ...
 };
 
 let p1 = new Promise((resolve, reject) => {
     getToken(); 
 });
 
 p1 instanceof Promise;  // ==> true
 
 
 
 eg2 ================>> : 回家繼續深刻
 
     let p1 = new Promise((resolve, reject) => {
        resolve('success...');
    }).then(data => {
        console.log(data);
        return 100; // => return Promise.resolve(100); 沒有return均可以嗎?
    }).then(data => {
        console.log(data);
        return 200;  // // => return Promise.resolve(200);
    }).then(data => {
        console.log(data);
    });
   success,  100, 200
複製代碼

6.Promise.prototype.catch()

  • 是原型上的方法
  • resolve的時候去回調catch(捕獲異常)
  • 返回一個新的Promise實例,鏈式寫法,能夠then
  • 雖然then能夠寫倆個函數,可是咱們通常用catch去進行捕獲
複製代碼

7.Promise.prototype.finally()

  • 是原型上的方法
  • 不論是resolve,仍是reject都去執行finally,相似於 try {} catch(e) {} finally{}
  • 返回一個新的Promise實例,鏈式寫法,能夠then

8.Promise面試題

eg1: Promise的構造函數中代碼是同步執行的,可是then方法是異步執行的,then方法須要等到等到resolve函數執行時才獲得執行。

baijiahao.baidu.com/s?id=158428…

const promise = new Promise((resolve, reject) => {
      console.log(1);
      resolve();
      console.log(2);
  });
  
  promise.then(() => {
      console.log(3);
  });
  console.log(4);
  // ==> 1 2 4 3
  
複製代碼

eg2: Promise一旦執行了resolve函數後,就不會再執行reject和其餘的resolve函數了。一旦Promise執行了reject函數,將會被catch函數捕獲,執行catch中的代碼。

const promise = new Promise((resolve, reject) => {
        resolve('success1');
        reject('error1');
        resolve('success2');
        reject('error2');
    });
    
    promise.then(res => {
        console.log(res);
    }).catch(err => {
        console.log(err);
    });
    
    //==> successs1

複製代碼

eg3: 題目中Promise首先resolve(1),接着就會執行then函數,所以會輸出1,而後在函數中返回2。

注意: return 返回基本數據類型,其實就是 Promise.resolve(value), 若是是返回引用數據類型會沖掉,返回這個對象

const p1 = Promise.resolve(1).then(res=> {
        console.log(res);
        return 2;  // ==>> return Promise.resolve(2);
        
        return Promise.resolve({
            a: 1,
            b: 2
        });
        
        return Promise.reject('error');
    }).catch(err => {
        console.log(err);
    }).then(res=> {
        console.log(res);
    }).catch(err => {
        console.log(err);
    });
    
    p1 instanceof Promise; // ==> true
    
複製代碼

eg4: 經典 setTime/promise (blog.csdn.net/baidu_33295…)

//==> 定時器是隊列
    setTimeout(function(){
        console.log(1);
    }, 0)
    
    // ==>>執行
    new Promise(function executor(resolve){
        console.log(2);
        for(var i = 0; i < 1000; i++){
            i = 9999 && resolve(); // ==>> 放到主線程後面
        }
        console.log(3);
    }).then(function(){
        console.log(4);
    })
    
    console.log(5);
    
    2 3  5 4 1
    
    詳解:
    以前說過,在定時器,事件,ajax等操做的時候,會使一個異步操做,會把該操做放到一個task queue裏,須要等當前主線程的任務完成後,會讀取任務隊列(task queue)中的是事件。
    
    那麼,setTimeout會放到任務隊列中,代碼繼續往下走。 
    因此先輸出2 3。 
    promise中的then操做是放在<<執行棧>>,也就是主線程的最後。 
    那麼主線程會繼續往下走咯。 
    因此輸出 5 4 
    最後主線程的任務搞完了,纔會去執行task queue中的任務。 
    因此最後執行1
    
        setTimeout(() => {
            console.log(1)
        }, 0);
        
        let promse1 = new Promise((resolve, rject) => {
            console.log(2);
            resolve('ok');
            console.log(3);
        });
        
        promse1.then(res => {
            console.log(4);
        }).catch(err => {
            console.log(err);
        });

    console.log(5);
    
    
    
複製代碼

7、Generator函數 (生成器) Generator yield next

  • 解決異步,深度嵌套問題

1.語法:

function *show{
    yield 
 }
 
 function* show {
    yield 
 }
複製代碼

2.用法: 配合next關鍵字使用,返回yiled後面的值

function * fn() {
    yield 1;
    yield 2;
    yield 3;
    return 'ok';
}

let f = fn();
console.log(f);
Object.prototype.toString.call(f); //==> "[object Generator]"

f.next();  // ==> {value: 1, done:false}
f.next();  // ==> {value: 2, done:false}
f.next();  // ==> {value: 3, done:false}
f.next();  // ==> {value: 'ok', done:true}
f.next();  // ==> {value: undefined, done:true} 完成了

可使用f.next.value; // ==> 拿出來想要的值

複製代碼

3.自動輸出 迭代器 for - of

  • return後面的東西遍歷出不來,遍歷的是yield後面的東西
for (let key of f) {
    console.log(key); // ==> 1, 2, 3
}
複製代碼

4.Generator配合解構賦值使用

let [a, b, c, d] = fn();  
console.log(a, b, c, d); // ==> 1, 2, 3, undefined  將yield後面的值放到一個數組裏面了 
複製代碼

5.Generator配合拓展運算符使用

let [a, ...b] = fn();  
console.log(a, b); // ==> 1, [2, 3]  將yield後面的值放到一個數組裏面了 

console.log(...fn(); // ==> 1, 2, 3
複製代碼

6.Generator配合Array.from轉爲數組

let f = Array.from(fn()); //==> [1, 2, 3]

console.log([...fn()]);
複製代碼

7.Generator配合axios請求數據

function * fn() {
    let val = yield '3653223131';
     yield axios.get(`http//sfddd/${token}`);

}

let f = fn();   // ==>>出來一個Generator實例
let userName = f.next().value;   // ==>>拿到第一個yield的的值
f.next(userName).value.then(res => {   // ==>>將第一個的值傳遞給URL裏面的token, axios返回promise對象
    console.log(res);
});
複製代碼

8、async函數 async(異步)

1.語法:

async function fn() { // ==>表示異步,這個函數裏面有異步任務 let result = await xxx //==>表示後面結果須要等待 }

2.特色:

  • await 只能放到async函數中 (yield只能用在Generator函數中) 可是不是必須的

  • 相比Generator更加語義化

  • await 後面能夠是promise對象,也能夠是number,string, boolean,自動轉爲promise對象

  • async函數執行完畢返回promsie對象 默認不寫return的話, 因此還能夠繼續then執行下一步操做

  • 只要await語句後面的Promise狀態變爲reject,那麼整個async函數就會中斷執行 (優化使用try-catch)

  • async函數能夠沒有await指令和return,都沒有返回promise對象, 有return沒有await返回Promise.resolve(val);

  • async拋出錯誤,如何解決 ? 只有promise就要進行catch

    • 採用try - catch進行捕獲異常
    • Promise進行catch捕獲
    • 捕獲異常,代碼更加健壯
  • async函數徹底能夠看做多個異步操做,包裝成的一個 Promise對象,而await命令就是內部then命令的語法糖。resolve成了,數據返回到then裏面,在傳給前面定義的變量

  • await命令後面是一個 Promise 對象。若是不是,會被轉成一個當即resolve的 Promise 對象。 Promies.resolve();

eg1: 返回promise對象,沒有await狀況

async function fn() {
      return 'async'; // ==> return Promise.resolve('async');
  }
  
  var f = fn();
  f instanceof Promise; // ==> true
  f.then(res =>{
      console.log(res);  // ==> Promise{<resolved>: undefined}****
  }).catch(err =>{
       console.log(err);
  });
複製代碼

eg2:沒有return和await的狀況,

async function fn() {
   
}

var f = fn();
console.log(f instanceof Promise); // ==> true
f.then(res =>{
    console.log(res);  // ==> Promise{<resolved>: undefined}
}).catch(err =>{
     console.log(err);
});

複製代碼

eg3: 有一個reject值不執行了,中止了

async function fn() {
        await Promise.reject('fail');  
        // ==>>下面代碼都不執行了 
        await Promise.resolve('success');
        console.log(1)
    }
    
    var f = fn();
    f.then(res => {
        console.log(res);
    }).catch(err => {
        console.log(err); // ==> fail
    });
    
    
    1.try - catch進行捕獲
        async function fn() {
           try() {
                await Promise.reject('fail');  
           } catch(e){
               
           }
            // ==>>下面代碼都不執行了 
            await Promise.resolve('success');
            console.log(1)
        }

        var f = fn();
        f.then(res => {
            console.log(res);
        }).catch(err => {
            console.log(err); // ==> fail
        });
        
        
        
    2.Promise的catch進行捕獲
        async function fn() {
             await Promise.reject('fail');  
            // ==>>下面代碼都不執行了 
            await Promise.resolve('success');
            console.log(1)
        }

        var f = fn();
        f.then(res => {
            console.log(res);
        }).catch(err => {
            console.log(err); // ==> fail
        });
複製代碼

eg4: 讀取多個ajax,沒有關係的狀況下

const fs = require('fs');
    // ==> 封裝讀取文件函數
    function readFile(file) {
        return new Promise((resolve, reject) => {
            fs.readFile(file, (err, data) => {
                if (err) {
                    reject(err);
                } else {
                    resolve(data);
                }
            })
        });
    }
    async function fn() {
        // ==> 三個執都resolve了,纔去對應的執行賦值, 配合解構賦值
        let [a, b, c] = await Promise.all([
            './a.txt',
            './b.txt',
            './c.txt'
        ]);
        console.log(a.toString());
        console.log(b.toString());
        console.log(c.toString());
    }
    
    fn();

複製代碼
案例,用node的fs模塊讀取文件分別用callback, promise, generator,async進行對比

===> 分別有三個文本文件 a.txt、 b.txt、 c.txt進行讀取裏面的內功,而且依次輸出

1.callback進行讀取

const fs = require('fs'); //==> 導入fs模塊 
function fn1(callback) {
    fs.readFile('./a.txt', (err, data) => {
        if (err) {
            console.log(err);
            return;
        } else {
            console.log(data.toString());
            callback && callback(() => {
                fs.readFile('./c.txt', (err, data) => {
                    if (err) {
                        console.log(err);
                        return;
                    } else {
                        console.log(data.toString());
                    }
                })
            });
        }
    });
}

fn1((callback) => {
    fs.readFile('./b.txt', (err, data) => {
        if (err) {
            console.log(err);
            return;
        } else {
            console.log(data.toString());
            callback && callback();
        }
    });
});

複製代碼

// 2.promise讀取文件

function readFile(file) {
    return new Promise((resolve, reject) => {
        fs.readFile(file, (err, data) => {
            if (err) {
                reject(err);
            } else {
                resolve(data);
            }
        })
    });
}

readFile('./a.txt').then(res => {
    console.log(res.toString());
    return readFile('./b.txt');
}).catch(err => {
    console.log(err);
}).then(res => {
    console.log(res.toString());
    return readFile('./c.txt');
}).catch(err => {
    console.log(err);
}).then(res => {
    console.log(res.toString());
}).catch(err => {
    console.log(err);
});
複製代碼

3.Generator讀取文件

const fs = require('fs');
function readFile(file) {
   return new Promise((resolve, reject) => {
       fs.readFile(file, (err, data) => {
           if (err) {
               reject(err);
           } else {
               resolve(data);
           }
       })
   });
}

function *gen() {
   yield readFile('./a.txt');
   yield readFile('./b.txt');
   yield readFile('./c.txt');
}


let f = gen(); //==>>先執行Generator返回迭代器

//==> console.log(s = f.next().value); Promise對象 狀態是pending
var s = f.next().value.then(res => { // f.next去執行   readFile('./a.txt'); 返回一個Promise實例進行then和catch
   console.log(res.toString());
    return f.next().value;   
}).catch(err =>{
   console.log(err);
}).then(res =>{
   console.log(res.toString());
   return f.next().value;  
}).catch(err =>{
   console.log(res);
}).then(res =>{
   console.log(res.toString());
}).catch(err =>{
   console.log(res);
});
複製代碼

4.async函數讀取文件

const fs = require('fs');
function readFile(file) {
    return new Promise((resolve, reject) => {
        fs.readFile(file, (err, data) => {
            if (err) {
                reject(err);
            } else {
                resolve(data);
            }
        })
    });
}

async function gen() {
  // ==> 捕獲異常,代碼更加健壯
  try {  
    let file1 = await readFile('./a.txt');
    console.log(file1.toString());

    let file2 = await readFile('./b.txt');
    console.log(file2.toString());

    let file3 = await readFile('./c.txt');
    console.log(file3.toString());
      
  } catch(e) {
      
  }

}
var s = gen();
console.log(s); // ==> promise對象
複製代碼

5.Promise.all方法去讀取,三個都成功了 resolve,狀態才進行返回,一個失敗了直接catch (這是沒有關係哦!!!三個文件順序的狀況下)

const fs = require('fs');
function readFile(file) {
    return new Promise((resolve, reject) => {
        fs.readFile(file, (err, data) => {
            if (err) {
                reject(err);
            } else {
                resolve(data);
            }
        })
    });
}

Promise.all([readFile('./a.txt'), readFile('./b.txt'), readFile('./c.txt')]).then(res =>{
    let [f1, f2, f3] = res;
    console.log(f1.toString(), f2.toString(), f3.toString());
}).catch(err => {
    console.log(err);
});

複製代碼

讀取文件:

const fs = require('fs');

class File {
    static readFile(fileName) {
        if (fileName && fileName.length) {
            let promise = new Promise((resolve, reject) => {
                fs.readFile(fileName, 'utf-8', (err, data) => {
                    if (err) {
                        console.log(err);
                    }
                    resolve(data);
                });
            });
            return promise;
        }
    }
}

// 1. Promies讀取文件
Promise.all([
   File.readFile('./b'),
   File.readFile('./test')
]).then(res => {
    let [a, b] = res;
    console.log(a);
    console.log(b);
});


//2. async配合Promise讀取文件

const asyncReadFile = async function() {
     // 2.1分開寫
    let a = await File.readFile('./b');
    console.log(a);

    let b = await File.readFile('./test');
    console.log(b);

    // 2.2 Promise.all()寫到一塊
    let [x, y] = await  Promise.all([File.readFile('./b'), File.readFile('./test')]);
    console.log(x);
    console.log(y);
    return 10;
};
let s = asyncReadFile();
console.log(s);

複製代碼
相關文章
相關標籤/搜索