美文网首页
JS异步解决方案的发展流程(一)

JS异步解决方案的发展流程(一)

作者: 那一抹阳光_d3b9 | 来源:发表于2018-02-18 11:38 被阅读0次

    所谓"异步",简单说就是一个任务分成两段,先执行第一段,然后转而执行其他任务,等做好了准备,再回过头执行第二段,比如,在我们烧水时可以干很多事情,当水烧开后在用水洗脸。这种不连续的执行,就叫做异步。相应地,连续的执行,就叫做同步。例如在烧水的过程中我们一直等待水烧开而不去干别的事情。

    1.高阶函数

    函数作为一等公民,可以作为参数和返回值
    高阶函数至少满足以两个条件

    • 接受一个或多个函数作为输入
    • 输出一个函数

    1.1 预置参数

    let isType = function(type,obj){
        return Object.prototype.toString.call(obj) ===`[object ${type}]`
    }
    console.log(isType('Object',{}));
    console.log(isType('Object',{}));
    console.log(isType('String','hello'));
    console.log(isType('String','hello'));
    // 我们发现每次调用isType都需要传入类型,所以我们可以先批量产出可供调用的函数
    let isType = function(type){
        return function(obj){
            return Object.prototype.toString.call(obj) ===`[object ${type}]`
        }
    }
    let isObject = isType('Object');
    let isString = isType('String');
    console.log(isObject({}));
    console.log(isObject({}));
    

    1.2 预置函数

    function after(times,cb){
        return function(){
            if(--times === 0){
                cb();
            }
        }
    }
    let eat = after(3,function(){console.log('吃完了')});
    eat();
    eat();
    eat();
    // 当调用次数达到我们预置的次数时,执行我们预置的函数
    

    到此我们对函数又有了进一步的认识,下面我们就来开始进入异步的解决方案。

    2.回调函数

    所谓回调函数,就是把任务的第二段单独写在一个函数里面,等到重新执行这个任务的时候,就直接调用这个函数。我们介绍一个常见的node中的异步方法readFile可以用来读取文件。

    fs.readFile(filename, function (err, data) {
      if (err) throw err;
      console.log(data);
    });
    

    在node中回调函数的第一个参数是错误对象(error-first callbacks)

    2.1 回调函数的应用

    function read(callback){
        setTimeout(function(){
            let result = 'zpfx';
            callback(result);
        })
    }
    read(function(data){
        console.log(data);
    });
    

    我们可以利用回调函数来解决异步问题

    3.回调的问题

    • 异步不支持try/catch,回调函数是在下一事件环中取出,所以一般在回调函数的第一个参数预置错误对象
    • 回调地狱问题,异步多级依赖的情况下嵌套非常深,代码难以阅读的维护
    • 多个异步在某一时刻获取所有异步的结果
    • 结果不能通过return返回

    4.Promise

    Promise本意是承诺,在程序中的意思就是承诺我过一段时间后会给你一个结果。 什么时候会用到过一段时间?答案是异步操作,异步是指可能比较长时间才有结果的才做,例如网络请求、读取本地文件等

    4.1 Promise的三种状态

    例如媳妇说想买个包,这时候他就要"等待"我的回复,我可以过两天买,如果买了表示"成功",如果我
    最后拒绝表示"失败",当然我也有可能一直拖一辈子

    • Pending Promise对象实例创建时候的初始状态
    • Fulfilled 可以理解为成功的状态
    • Rejected 可以理解为失败的状态

    then 方法就是用来指定Promise 对象的状态改变时确定执行的操作,resolve 时执行第一个函数
    (onFulfilled),reject 时执行第二个函数(onRejected)

    4.2 构造Promise

    promise的方法会立刻执行

    let promise = new Promise(() => {
        console.log('hello');
    }); 
    console.log('world'); 
    // hello
    // world
    

    4.3 promise也可以代表一个未来的值

    const fs = require('fs');
    let  promise = new Promise((resolve, reject) => {
        fs.readFile('./content.txt', 'utf8', function (err, data)  {
            if (err) return reject(err); 
            resolve(data);
        })
     }); 
    promise.then(data => {
        console.log(data);  
    });
    promise.then(data => {
        console.log(data);
    });
    // 一个promise实例可以多次调用then当成功后会将结果依次执行
    

    4.4 代表一个用于不会返回的值

    const fs = require('fs');
    let  promise = new Promise((resolve, reject) => { });
    promise.then(data => {
        console.log(data); //代表一个用于不会返回的值
    }); 
    

    4.5 实现买包的案例

    function buyPack()  {
        return new Promise((resolve, reject) => {
            setTimeout(function  ()  {
                var  random = Math.random();
                if (random > 0.5) {
                    resolve('买');
                } else {
                    resolve('不买');
                }
            }, 2000)
        })
    }
    buyPack().then(data => {
        console.log(data);
    }, data => {
        console.log(data);
    }); 
    

    4.6 Error会导致触发Reject

    可以采用then的第二个参数捕获失败,也可以通过catch函数进行

    function buyPack()  {
        return new Promise((resolve, reject) => {
            throw new Error('没钱')
        })
    }
    buyPack().then(data => {
        console.log(data);
    }, data => {
        console.log(data);
    }); 
    

    5.解决回调地狱

    回归正题,先用promise解决第一个问题"回调地狱"

    // 1.txt => 2.txt
    // 2.txt => 我很帅
    let fs = require('fs');
    function read(){
        fs.readFile('./1.txt','utf8',function(err,data){
            if(err) return console.log(err);
            fs.readFile(data,'utf8',function(err,data){
                if(err) return console.log(err);
                console.log(data); // 我很帅
            })
        })
    }
    read();
    

    改写Promise形式

    let fs = require('fs');
    function read(file){
        return new Promise(function(resolve,reject){
            fs.readFile(file,'utf8',function(err,data){
                if(err) return reject(err);
                resolve(data);
            })
        })
    }
    read('./1.txt').then(function(data){
        return read(data);
    }).then(function(data){
        console.log(data)
    }).catch(function(err){
        console.log(err)
    });
    

    当第一个then中返回一个promise,会将返回的promise的结果,传递到下一个then中。这就是比较著名的链式调用了。

    6.同步异步的结果

    我们将多个异步请求的结果在同一时间进行汇总

    // 1.txt => template
    // 2.txt => data
    let fs = require('fs');
    let result = {}
    function out(key,data) {
        result[key] = data;
        if(Object.keys(result).length === 2){
            console.log(result)
        }
    }
    fs.readFile('./1.txt', 'utf8', function (err, data) {
        if (err) return console.log(err);
        out('template',data);
    })
    fs.readFile('./2.txt', 'utf8', function (err, data) {
        if (err) return console.log(err);
        out('data',data);
    });
    // 这种方式并不好,要声明全局的对象,并且成功的数量也是写死的,我们可以使用偏函数来进行改写
    
    let fs = require('fs');
    let result = {}
    function after(times,cb) {
        let result = {}
        return function(key,data){
            result[key] = data;
            if(Object.keys(result).length === times){
                cb(result)
            }
        }
    }
    let out = after(2,function(data){
        console.log(data)
    })
    fs.readFile('./1.txt', 'utf8', function (err, data) {
        if (err) return console.log(err);
        out('template',data);
    })
    fs.readFile('./2.txt', 'utf8', function (err, data) {
        if (err) return console.log(err);
        out('data',data);
    });
    

    最后我们可以采用Promise.all方法来进行简化

    let fs = require('fs');
    function read(file){
        return new Promise(function(resolve,reject){
            fs.readFile(file,'utf8',function(err,data){
                if(err) return reject(err);
                resolve(data);
            })
        })
    }
    Promise.all([read('1.txt'),read('2.txt')]).then(([template,data])=>{
        console.log({template,data})
    });
    // 不管两个promise谁先完成,Promise.all 方法会按照数组里面的顺序将结果返回
    

    7.Promise.race

    接受一个数组,数组内都是Promise实例,返回一个Promise实例,这个Promise实例的状态转移取决于参数的Promise实例的状态变化。当参数中任何一个实例处于resolve状态时,返回的Promise实例会变为resolve状态。如果参数中任意一个实例处于reject状态,返回的Promise实例变为reject状态。

    Promise.race([read('1.txt'),read('2.txt')]).then(data=>{
        console.log({template,data})
    },(err)=>{
        console.log(err)
    });
    

    8.Promise.resolve

    返回一个Promise实例,这个实例处于resolve状态

    Promise.resolve('成功').then(data=>{ 
        console.log(data);
    });
    

    9.Promise.reject

    返回一个Promise实例,这个实例处于reject状态

    Promise.reject('失败').then(data=>{ 
       console.log(data); 
    },err=>{ 
    console.log(err); 
    }) 
    

    下一节我们会继续介绍generator和async/await的用法,以及Promise的实现原理,喜欢的点个赞吧_!
    支持我的可以给我打赏

    打赏

    相关文章

      网友评论

          本文标题:JS异步解决方案的发展流程(一)

          本文链接:https://www.haomeiwen.com/subject/xoqvtftx.html