异步那些事

简介: ajax,axios,回调地狱,Generator,promise,async/awiat

异步请求

JS中ajax原生请求

var xhr;
xhr=new XMLHttpRequest();
open('GET','url地址')
xhr.onreadyStateChange=function(){
  if(xhr.readyState==4&&xhe.status==200){
    console.log(xhr.responseText);
  }
};
send();

封装后的ajax请求

GET

$.ajax({
  type:'get',//不写默认get
  url:'index.php'
  data:{
     name:'jim',
     age:10
   }//data中的数据也可以当作参数写在url后面
  success:function(data){
    console.log(data)
   }
})

POST

$.ajax({
  type:'post',
  url:'',
  data:{
    name:'jim',
    age:10
  },
  success:function(info){
    console.log(info)
  }
})

Vue中的axios请求

  • Vue没有任何ajax请求方法
  • 在vue1时代大家都用一个插件vue resource来发请求

    • 支持promise
  • 在Vue2.0时代,大家都用第三方库axios

    • 支持promise
  • 后来在HTML5时代,浏览器增加了一个特殊的异步请求,fetch,只在移动端使用
  • 结合声明钩子获取数据

安装

npm install axios

引入

import axios from 'axios'

执行GET请求

// 为给定 ID 的 user 创建请求
axios.get('/user?ID=12345')
  .then(function (response) {
    console.log(response);
  }) //then promise语法,下面有具体描述
  .catch(function (error) {
    console.log(error);
  });

// 可选地,上面的请求可以这样做
axios.get('/user', {
    params: {
      ID: 12345
    }
  })
  .then(function (response) {
    console.log(response);
  })
  .catch(function (error) {
    console.log(error);
  });//catch 捕捉错误对象

POST请求

axios.post('/user', {
    firstName: 'Fred',
    lastName: 'Flintstone'
  })
  .then(function (response) {
    console.log(response);
  })
  .catch(function (error) {
    console.log(error);
  });

执行多个并发请求

function getUserAccount() {
  return axios.get('/user/12345');
}

function getUserPermissions() {
  return axios.get('/user/12345/permissions');
}

axios.all([getUserAccount(), getUserPermissions()])
  .then(axios.spread(function (acct, perms) {
    // 两个请求现在都执行完成
  }));

可以通过axios传递相关配置来创建请求

axios(config)

//发送post请求
axios({
  method: 'post',
  url: '/user/12345',
  data: {
    firstName: 'Fred',
    lastName: 'Flintstone'
  }
});

axios(url[, config])

// 发送 GET 请求(默认的方法)
axios('/user/12345');

异步操作

回调地狱

**以同时读取文件为例,因为是异步请求无法保证顺序,快慢取决于读取的文件大小.

无法保证顺序的代码*

var fs = require('fs')

fs.readFile('./data/a.txt', 'utf8', function (err, data) {
  if (err) {
    // return console.log('读取失败')
    // 抛出异常
    //    1. 阻止程序的执行
    //    2. 把错误消息打印到控制台
    throw err
  }
  console.log(data)
})

fs.readFile('./data/b.txt', 'utf8', function (err, data) {
  if (err) {
    // return console.log('读取失败')
    // 抛出异常
    //    1. 阻止程序的执行
    //    2. 把错误消息打印到控制台
    throw err
  }
  console.log(data)
})

fs.readFile('./data/c.txt', 'utf8', function (err, data) {
  if (err) {
    // return console.log('读取失败')
    // 抛出异常
    //    1. 阻止程序的执行
    //    2. 把错误消息打印到控制台
    throw err
  }
  console.log(data)
})

通过回调嵌套的方式保证顺序

var fs = require('fs')

fs.readFile('./data/a.txt', 'utf8', function (err, data) {
  if (err) {
    // return console.log('读取失败')
    // 抛出异常
    //    1. 阻止程序的执行
    //    2. 把错误消息打印到控制台
    throw err
  }
  console.log(data)
  fs.readFile('./data/b.txt', 'utf8', function (err, data) {
    if (err) {
      // return console.log('读取失败')
      // 抛出异常
      //    1. 阻止程序的执行
      //    2. 把错误消息打印到控制台
      throw err
    }
    console.log(data)
    fs.readFile('./data/c.txt', 'utf8', function (err, data) {
      if (err) {
        // return console.log('读取失败')
        // 抛出异常
        //    1. 阻止程序的执行
        //    2. 把错误消息打印到控制台
        throw err
      }
      console.log(data)
    })
  })
})
  • 但是这样不停的嵌套很不好重复代码太多,反复的嵌套,最后形成回调地狱
  • 所以在 EcmaScript 6 中新增了一个 API:Promise。

Promise 基本语法

var fs = require('fs')

// 在 EcmaScript 6 中新增了一个 API Promise
// Promise 是一个构造函数

// 创建 Promise 容器
// Promise 容器一旦创建,就开始执行里面的代码
var p1 = new Promise(function (resolve, reject) {
  fs.readFile('./data/aa.txt', 'utf8', function (err, data) {
    if (err) {
      // 失败了,承诺容器中的任务失败了
      // console.log(err)
      // 把容器的 Pending 状态变为 Rejected

      // 调用 reject 就相当于调用了 then 方法的第二个参数函数
      reject(err)
    } else {
      // 承诺容器中的任务成功了
      // console.log(data)
      // 把容器的 Pending 状态改为成功 Resolved
      // 也就是说这里调用的 resolve 方法实际上就是 then 方法传递的那个 function
      resolve(data)
    }
  })
})

// p1 就是那个承若
// 当 p1 成功了 然后(then) 做指定的操作
// then 方法接收的 function 就是容器中的 resolve 函数
p1
  .then(function (data) {
    console.log(data)
  }, function (err) {
    console.log('读取文件失败了', err)
  })

综合以上封装刚才的readFile文件

var fs = require('fs')

function pReadFile(filePath) {
  return new Promise(function (resolve, reject) {
    fs.readFile(filePath, 'utf8', function (err, data) {
      if (err) {
        reject(err)
      } else {
        resolve(data)
      }
    })
  })
}

pReadFile('./data/a.txt')
  .then(function (data) {
    console.log(data)
    return pReadFile('./data/b.txt')
  })
  .then(function (data) {
    console.log(data)
    return pReadFile('./data/c.txt')
  })
  .then(function (data) {
    console.log(data)
  })

Generator函数的概念

Generator 函数是协程在 ES6 的实现,最大特点就是可以交出函数的执行权(即暂停执行)

function *gen(x){
  var y=yied x+2;
  return y;
}
  • 上面的代码就是Generator函数,他是可以暂停的,所以函数名之前要加*加以区别;
  • 整个Generator函数就是一个封装的异步任务,或者说是异步任务的容器,异步操作需要暂停的地方,都用yield语句注明.执行方法如下:

    var g=gen(1);
    g.next()//{value:3,done:fasle}
    g.next()//{value:undefined,done:true}

  • 上面代码中,调用 Generator 函数,会返回一个内部指针(即遍历器 )g 。这是 Generator 函数不同于普通函数的另一个地方,即执行它不会返回结果,返回的是指针对象。调用指针 g 的 next 方法,会移动内部指针(即执行异步任务的第一段),指向第一个遇到的 yield 语句,上例是执行到 x + 2 为止。
  • 换言之,next 方法的作用是分阶段执行 Generator 函数。每次调用 next 方法,会返回一个对象,表示当前阶段的信息( value 属性和 done 属性)。value 属性是 yield 语句后面表达式的值,表示当前阶段的值;done 属性是一个布尔值,表示 Generator 函数是否执行完毕,即是否还有下一个阶段。

Async/Awiat 配合promise使用

阅读本文前,期待您对上面promise以及ES6有所了解,会更容易理解。

本文以体验为主,不会深入说明.

第一个例子

  • Async/Await应该是目前最简单的异步方案了
  • 这里我们要实现一个暂停功能,输入N毫秒,则停顿N毫秒后才继续往下执行。

    var sleep = function (time) {

    return new Promise(function (resolve, reject) {
        setTimeout(function () {
            resolve();
        }, time);
    })

    };

    var start = async function () {

    // 在这里使用起来就像同步代码那样直观
    console.log('start');
    await sleep(3000);
    console.log('end');

    };

    start();

控制台先输出start,稍等3秒后,输出了end。

基本规则

  1. async 表示这是一个async函数,await只能用在这个函数里面。
  2. await 表示在这里等待promise返回结果了,再继续执行。
  3. await 后面跟着的应该是一个promise对象(当然,其他返回值也没关系,只是会立即执行,不过那样就没有意义了…)

获得返回值

  • await等待的虽然是promise对象,但不必写.then(..),直接可以得到返回值。

    var sleep = function (time) {

    return new Promise(function (resolve, reject) {
        setTimeout(function () {
            // 返回 ‘ok’
            resolve('ok');
        }, time);
    })

    };

    var start = async function () {

    let result = await sleep(3000);
    console.log(result); // 收到 ‘ok’

    };


捕捉错误

  • 既然.then(..)不用写了,那么.catch(..)也不用写,可以直接用标准的try catch语法捕捉错误。

    var sleep = function (time) {

    return new Promise(function (resolve, reject) {
        setTimeout(function () {
            // 模拟出错了,返回 ‘error’
            reject('error');
        }, time);
    })

    };

    var start = async function () {

    try {
        console.log('start');
        await sleep(3000); // 这里得到了一个返回错误
        
        // 所以以下代码不会被执行了
        console.log('end');
    } catch (err) {
        console.log(err); // 这里捕捉到错误 `error`
    }

    };

补充try catch

  • try 语句允许我们定义在执行时进行错误测试的代码块。
  • catch 语句允许我们定义当 try 代码块发生错误时,所执行的代码块。
  • JavaScript 语句 try 和 catch 是成对出现的。

    try
      {
      //在这里运行代码
      }
    catch(err)
      {
      //在这里处理错误
      }
    

在这里我们故意在try中写错一个字,catch会捕捉到try中的错误,并执行代码处理它;

<!DOCTYPE html>
<html>
<head>
<script>
var txt="";
function message()
{
try
  {
  adddlert("Welcome guest!");
  }
catch(err)
  {
  txt="There was an error on this page.\n\n";
  txt+="Error description: " + err.message + "\n\n";
  txt+="Click OK to continue.\n\n";
  alert(txt);
  }
}
</script>
</head>

<body>
<input type="button" value="View message" onclick="message()">
</body>

</html>

弹出窗为:

本页有一个错误.
错误描述:adddlert is not defined
点击确定继续

总的来说就是try里面放要运行的代码,catch里面放处理错误的代码;

补充throw

  • 抛出异常
  • 错误可以自定义

三者混合用:

<!DOCTYPE html>
<html>
<body>

<script>
function myFunction()
{

try
{ 
var x=document.getElementById("demo").value;
if(x=="")    throw "值为空";
if(isNaN(x)) throw "不是数字";
if(x>10)     throw "太大";
if(x<5)      throw "太小";
}

catch(err)
{
var y=document.getElementById("mess");
y.innerHTML="错误:" + err + "。";
}
}

</script>

<h1>我的第一个 JavaScript 程序</h1>
<p>请输入 5 到 10 之间的数字:</p>
<input id="demo" type="text">
<button type="button" onclick="myFunction()">测试输入值</button>
<p id="mess"></p>

</body>
</html>

以上就是自己总结的,如有错误欢迎指出,时间太紧,没有太详细.主要还是当作自己的随笔笔记,总结为主.哈哈哈~

相关文章
|
前端开发 JavaScript UED
|
前端开发
异步转同步的几种方法
在循环等待中,我们可以使用一个变量来指示异步操作是否已完成。然后,我们可以在循环中检查该变量,如果它指示异步操作已完成,则退出循环。
570 0
|
29天前
|
前端开发 JavaScript API
前端:事件循环/异步
前端开发中的事件循环和异步处理是核心机制,用于管理任务执行、性能优化及响应用户操作,确保网页流畅运行。事件循环负责调度任务,而异步则通过回调、Promise等实现非阻塞操作。
|
4月前
|
数据采集 Python
多线程和异步
【8月更文挑战第12天】
45 3
|
7月前
|
负载均衡 算法 前端开发
同步和异步
同步和异步
95 0
|
7月前
|
存储 JavaScript 前端开发
|
C#
C#异步详解
c#异步编程原理,await asnyc的使用方法
68 0
同步和异步[多线程的异步执行操作]
同步和异步[多线程的异步执行操作]
58 0
3 # 通过回调函数处理异步并发问题
3 # 通过回调函数处理异步并发问题
53 0
|
存储 SQL 设计模式
C#异步有多少种实现方式?
C#异步有多少种实现方式?