异步
1. Promise、then
ES6 中引入,解决了 ES5 地狱回调的问题
同步和异步的概念
-
同步:代码从上往下执行,遇到同步,就会一直等,等到完成才会向下走
-
异步:代码从上往下执行,遇到异步,就先走了,等到异步发出完成信号,才回来处理(调用相应回调函数)
【例子】
三次 ajax 异步回调,依次访问 1,2,3 的 url,成功后才访问下一个
-
地狱回调
$.ajax({ url: "1", success: function (res) { console.log("结果1" + res); $.ajax({ url: "2", success: function (res) { console.log("结果2" + res); $.ajax({ url: "3", success: function (res) { console.log("结果3" + res); }, }); }, }); }, }); -
Promise 例子
let p1 = new Promise(function(resolve,reject){ $.ajax({ url:"1", success:function(res){ resolve(res) //成功后发出信号,携带res内容,就会触发下面then之后的回调 } }) } p1 .then(function(res){ console.log("结果1" + res); let p2 = new Promise(跟上面一样写,也是成功后发出信号); return p2; //记得返回,作为下一个then函数的执行对象 }) .then(function(res){ console.log("结果2" + res); let p3 = new Promise(跟上面一样写,也是成功后发出信号); return p3; //记得返回,作为下一个then函数的执行对象 }) .then(function(res){ console.log("结果3" + res); })- 通过 new 一个 Promise 对象,把每一次的异步调用都封装起来,等到发出 resolve 或者 reject 信号,才执行对象的 then 方法,代码变得很优雅!
还支持 all 和 race
-
all:多个 Promise 都触发信号,才能往下执行 then 方法
.then(function(res){ let p4 = new Promise() let p5 = new Promise() return Promise.all([p4,p5]) }) .then() -
race: 只要有一个触发信号,就能往下执行 then 方法
.then(function(res){ let p6 = new Promise() let p7 = new Promise() return Promise.race([p6,p7]) }) .then()
在 Promise 的协助下,就能封装出 axios
-
ajax 缺点
- 本身针对 MVC 编程,不符合现在 MVVM 的浪潮
- 基于原生 XHR 开发,架构不清晰,已经有了 fecth 的替代方案
- 来自 jquery,只用到 ajax 却要引入整个库,不划算
-
axios 改进点
- 提供了一些并发请求接口
axios.all([axios.get("data.json"), axios.get("city.json")]).then();
- 提供了一些并发请求接口
2. async、await
ES7 中引入,让 ES6 的 promise 可读性更强
async 是什么?
-
声明
会返回Promise的函数 -
两句话
- async 就是声明“后面定义的函数”为异步函数
- 这样才能在别的 async 函数体里面调用这个异步函数,并在它前面使用 await 去等待 Promise 兑现
//ES6写法
function s() {
return Promise.resolve("TEST");
}
function f() {
return Promise.reject("Error");
}
//ES7写法
async function asyncS() {
return "TEST";
}
async function asyncF() {
throw "Error";
}
本质就是 Generator 函数的语法糖
await 是什么?
只能在声明了是 async 的函数内使用 让我们能等待一个 Promise 返回信号,返回后才能继续往下执行
【例子】
-
模拟异步
- 用 setTimeout 模拟异步,并用 Promise 封装
let func = (val) => { return new Promise((resolve, reject) => { setTimeout(() => { resolve(val); }, 1000); }); };
- 用 setTimeout 模拟异步,并用 Promise 封装
-
定义异步函数
- 下面调用 func 会返回一个 Promise 对象,可以加 await 等待
async function f() { const response = await func("http://example.com/"); //下面就相当于Promise返回后then里面的回调,使得异步代码能够用同步逻辑去书写 console.log(response); }
- 下面调用 func 会返回一个 Promise 对象,可以加 await 等待
-
执行
- 由于在最外层不能用 await,所以需要用下面两种方法
- 【办法 1】用.then 写 Promise 返回后的回调
f().then(() => console.log("Finished")); - 【办法 2】用立即执行函数
(async () => { await f(); console.log("Finished"); })();
-
如何对 await 捕获异常
- 使用
try catch,包裹住await 函数即可
- 使用
Generator 函数是什么?
-
它可以断续执行,并返回一连串的值
-
函数内书写一行行的“yeild 返回值”来定义返回(每一行可返回一次),并停止执行
加上星号来定义; function* helloWorldGenerator() { yield "hello"; yield "world"; return "ending"; } -
调用此函数生成对象,再调用对象的
.next方法来恢复执行,并触发一次 yield 返回- 生成对象
var hw = helloWorldGenerator(); - 调用对象中的 next 方法
console.log(hw.next()); //返回{value:"hello",done:false}- 这里
- value 代表 yield 返回的值
- done 代表还可以继续调用.next()得到返回值
- 这里
- 再次调用
console.log(hw.next()); //返回{value:"world",done:false} - 再次调用
console.log(hw.next()); //返回{value:"ending",done:true}- 发现 done 为 true,即可知道生成器执行到最后的 return 了
- 生成对象
3. JSONP
-
原理
- 在 DOM 中添加一个 script 标签,标签会向其 src 请求一个脚本
- 返回的脚本含有“调用本地的某个函数”的代码,从而实现异步
-
例子
function jsonpFunc(data) { console.log(data); } function request_jsonp() { var script = document.createElement("script"); script.type = "text/javascript"; script.src = "https://api.xxx.com/jsonp?callback=jsonpFunc"; document.head.appendChild(script); } request_jsonp();- 会返回以下脚本,给予参数并调用本地函数
jsonpFunc({ name: "Tom", });
- 会返回以下脚本,给予参数并调用本地函数
-
特点
- 可以跨域
4. setTimeout
-
原理
- 依赖事件循环,在一定时间后执行回调函数
-
例子
setTimeout(() => { console.log("delay 1 second"); }, 1000);- 注意单位是毫秒