美文网首页
js 异步系列(1) -发展

js 异步系列(1) -发展

作者: Super曲江龙Kimi | 来源:发表于2019-08-18 10:03 被阅读0次

    ajax

    最早为了实现局部请求无刷新,有了ajax的概念。在原生js是时代,请求数据都需要自己手写一个ajax。现在还有很多面试题会让手写一个ajax

    // 先实例化一个XMLHttpRequest对象
    const xhr = new XMLHttpRequest()
    
    // xhr 具有一个 open 方法,相当于填写配置先初始化。这时候没有发出请求
    // method: 请求方式 —— get / post
    // url:请求的地址
    // async:是否异步请求,默认为 true(异步)
    xhr.open(method, url, async)
    
    // send 方法发送请求,并接受一个可选参数
    // 当请求方式为 post 时,可以将请求体的参数传入
    // 当请求方式为 get 时,可以不传或传入 null
    xhr.send(data)
    
    // xhr上有一个readyStatus值。他会随着请求的阶段不同而变化:
    // xhr.readyStatus==0 尚未调用 open 方法
    // xhr.readyStatus==1 已调用 open 但还未发送请求(未调用 send)
    // xhr.readyStatus==2 已发送请求(已调用 send)
    // xhr.readyStatus==3 已接收到请求返回的数据
    // xhr.readyStatus==4 请求已完成
    
    // 当readyStatus变化时都会执行onreadystatechange 回调。
    // responseText: 请求返回的数据内容
    // status: 响应的HTTP状态,如 200 304 404 等
    xhr.onreadystatechange = () => {
        if (xhr.readyStatus === 4) {
            if (xhr.status >= 200 && 
                xhr.status < 300 || 
                xhr.status == 304) {
                console.log('请求成功', xhr.responseText)
            }
        }
    }
    
    // 设置超时时间为1000毫秒
    xhr.timeout = 1000
    

    jquery

    后来在jquery盛行的时代。将ajax封装成了一个函数。可以直接调用

    $.ajax({  
      type : "POST",
      url:'http://aaaa.com',
      async:true,
      dataType:'json',
      data:{a:'aa'},
      success :function(msg){
          //请求成功函数
      },
      error:function(err){
          //请求失败函数
      }
    });
    

    jquery1.5之后

    var ajax = $.ajax('data.json')
    ajax.done(function () {
            console.log('success 1')
        })
        .fail(function () {
            console.log('error')
        })
        .done(function () {
             console.log('success 2')
        })
    
    console.log(ajax) // 返回一个 deferred 对象
    

    区别:
    之前返回的是一个XHR对象,这个对象不可能有done或者fail的方法的
    1.5开始返回一个deferred对象,这个对象就带有done和fail的方法,并且是等着请求返回之后再去调用

    promise前身

    // 改造前
    var wait = function() {
        var task = function () {
            console.log('ok')
        }
        setTimeout(task, 2000)
    }
    wait()
    // 改造后  
    function waitHandle() {
        var dtd = $.Deferred()  
        var wait = function(dtd) {
            var task = function() {
                console.log('ok');
                dtd.resolve()
                // dtd.reject() 
            }
            setTimeout(task, 2000)
            return dtd.promise() // 返回promise对象,使用时不可以修改状态。promise的前身
        }
        
        return wait(dtd);
    }   
    waitHandle().then();    
    

    1.5改造之后最大的好处就是可以通过then的方法来链式调用。不再会有回调地狱。而且状态一旦改变,也不能再变成其他状态了。和之后的promise一样。

    promise

    es6中将之前社区中的方案正式纳入标准,制定了promise/A+规范。也就可以直接的使用promise

    const getJSON = function(url) {
      const promise = new Promise(function(resolve, reject){
        const handler = function() {
          if (this.readyState !== 4) {
            return;
          }
          if (this.status === 200) {
            resolve(this.response);
          } else {
            reject(new Error(this.statusText));
          }
        };
        const client = new XMLHttpRequest();
        client.open("GET", url);
        client.onreadystatechange = handler;
        client.responseType = "json";
        client.setRequestHeader("Accept", "application/json");
        client.send();
    
      });
    
      return promise;
    };
    
    getJSON("/posts.json").then(function(json) {
      console.log('Contents: ' + json);
    }, function(error) {
      console.error('出错了', error);
    });
    

    generator

    promise的出现只是回调函数的改进,使用then方法以后,异步任务的两段执行看得更清楚了,会造成代码冗余。一堆的then。如下

    var readFile = require('fs-readfile-promise');
    
    readFile(fileA)
    .then(function (data) {
      console.log(data.toString());
    })
    .then(function () {
      return readFile(fileB);
    })
    .then(function (data) {
      console.log(data.toString());
    })
    .catch(function (err) {
      console.log(err);
    });
    

    于是有了更好的解决方案---generator

    var fetch = require('node-fetch');
    
    function* gen(){
      var url = 'https://api.github.com/users/github';
      var result = yield fetch(url);
      console.log(result.bio);
    }
    
    var g = gen();
    var result = g.next();
    
    result.value.then(function(data){
      return data.json();
    }).then(function(data){
      g.next(data);
    });
    

    Generator 函数可以暂停执行和恢复执行,这是它能封装异步任务的根本原因。虽然 Generator 函数将异步操作表示得很简洁,但是流程管理却不方便,需要执行器`控制他什么时候执行第一步,什么时候执行第二步。并且传递参数。

    于是有了co这种流程管理库。

    简单的流程管理如下。会一直执行下去。

    function* gen() {
      // ...
    }
    
    var g = gen();
    var res = g.next();
    
    while(!res.done){
      console.log(res.value);
      res = g.next();
    }
    

    async await --终极方案

    async function getStockPriceByName(name) {
      const symbol = await getStockSymbol(name);
      const stockPrice = await getStockPrice(symbol);
      return stockPrice;
    }
    
    getStockPriceByName('goog').then(function (result) {
      console.log(result);
    });
    

    好处
    (1)内置执行器。
    Generator 函数的执行必须靠执行器,所以才有了co模块,而async函数自带执行器。也就是说,async函数的执行,与普通函数一模一样,只要一行。

    getStockPriceByName();
    

    上面的代码调用了getStockPriceByName函数,然后它就会自动执行,输出最后结果。这完全不像 Generator 函数,需要调用next方法,或者用co模块,才能真正执行,得到最后结果。

    (2)更好的语义。
    async和await,比起星号和yield,语义更清楚了。async表示函数里有异步操作,await表示紧跟在后面的表达式需要等待结果。

    (3)更广的适用性。
    co模块约定,yield命令后面只能是 Thunk 函数或 Promise 对象,而async函数的await命令后面,可以是 Promise 对象和原始类型的值(数值、字符串和布尔值,但这时会自动转成立即 resolved 的 Promise 对象)。

    (4)返回值是 Promise。
    async函数的返回值是 Promise 对象,这比 Generator 函数的返回值是 Iterator 对象方便多了。你可以用then方法指定下一步的操作。

    进一步说,async函数完全可以看作多个异步操作,包装成的一个 Promise 对象,而await命令就是内部then命令的语法糖。

    在nodejs中已经使用async 、 await做为异步操作的解决方案

    相关文章

      网友评论

          本文标题:js 异步系列(1) -发展

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