js异步处理

一.基础知识巩固

  • 同步(Synchronous): 代码依次向下执行,如果遇到请求获取其他的,等待执行完了,之后再执行后面的代码
  • 异步(Asychronous): 代码依次向下执行,遇到异步的代码(事件、Ajax、setTimeout、setInterval、Promise 、Node…无需中断,,则继续执行后面的代码,等到他们请求完毕,在回调里面去执行他们)
  • js单线程
  • js执行顺序,主线程 > 异步(ajax,promise,fetch)> 队列(setTimeout/setInterval)

二.异步出现情况

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

三.异步发展

  • 回调函数(callback) [回调地狱问题,无限回调,维护空难]

  • 事件监听(Listener)

  • 观察者模式(发布订阅模式) DOM二级绑定多个事件(addEventListener) 原理就是观察者模式 ,事件池概念出来,jQ多个on

  • Promise类

  • Generator函数

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

四.回调函数 (将要执行的 任务放到一个函数里面,作为参数去传给另一个函数,另一个函数执行完毕,去执行这个函数)

  • 简单,容易理解和 部署
  • 不利于代码的阅读,和维护,各部分之间高度耦合,流程会很混乱,而且每一个任务只能指定一个回调函数。

eg1:

// ==>> callback
   function fn(callback) {
     var x = 1;
   callback && callback(x); //函数fn里面的变量传给了他的回调里面

   }

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

eg2:

// ==>> 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:

// ==>> 回调地狱
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二级事件
    • 原理是发布订阅模式,将所有的事件放到一个事件池里面,循环依次去执行
    发布订阅模式,也就是观察者模式
   {'click': [fn1, fn2, fn3]}   click执行 ===>循环一一  fn1() fn2() fn3()
// 发布emit  订阅on {女生失恋: ['哭', '购物', '吃']}

// {'入职':[setEmail, getPc], '失恋':[cry,shopping,eat]}
// on绑定事件
// emit 触发事件
// off 事件解绑

function Girl() {
  this._event = {};  //自定义事件对象
}


Girl.prototype.on = function(eventName, callback) {
    if (eventName && eventName.length && callback) {
        if (this._event[eventName]) {
            this._event[eventName].push(callback);
        } else {
            this._event[eventName] = [];
            this._event[eventName].push(callback);
        }
    } else {
        return null;
    }
    return this;
};

Girl.prototype.emit = function(eventName) {
    if (eventName && eventName.length) {
        this._event[eventName].forEach(element => {
            element();
        });
    }
};


    
Girl.prototype.off = function(eventName, callback) {
    if(eventName && eventName.length && callback) {
        this._event[eventName].forEach((item, index) => {
            if (item === callback) {
                this._event[eventName].splice(index, 1);
                return false;
            }
        });
    }
    return this;
};




function cry() {
    console.log('哭');
}

function shopping() {
    console.log('购物');
}


function eat() {
    console.log('吃');

}


function  setEmail() {
    console.log('开通邮箱');
}


function getPc() {
    console.log('获取办公用品');
}



const girl = new Girl();
girl.on('失恋', cry);
girl.on('失恋', shopping);
girl.on('失恋', eat);


girl.on('入职', setEmail);
girl.on('入职', getPc);


girl.emit('失恋');
girl.emit('入职');


girl.off('失恋', shopping);
girl.off('失恋', eat);
girl.emit('失恋');`

六.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里面了
  • 只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。
  • 只要p1、p2、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函数执行时才得到执行。

https://baijiahao.baidu.com/s?id=1584286066043336871&wfr=spider&for=pc

  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 (https://blog.csdn.net/baidu_33295233/article/details/79335127)

    //==> 定时器是队列
    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);
    
    
    

七、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);
});

八、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);

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值