js解决异步编程有6种方案:
1.1 回调函数
异步编程的最基本方法,把任务的第二段单独写在一个函数里面,等到重新执行这个任务的时候,就直接调用这个函数。
- 优点:简单、容易理解和实现。
- 缺点:多次调用会使代码结构混乱,形成回调地狱。
function sleep(time, callback) {
setTimeout(() => {
// 一些逻辑代码
callback();
}, time);
}
1.2 事件监听
异步任务的执行不取决于代码的执行顺序,而取决于某个事件是否发生。
- 优点:易于理解,此外对于每个事件可以指定多个回调函数,而且可以“去耦合”,有利于实现模块化。
- 缺点:整个程序都要变成事件驱动型,运行流程会变得很不清晰。
dom.addEventListener('click', () => {
console.log('dom被点击后触发!!!');
})
1.3 发布/订阅
发布/订阅模式在观察者模式的基础上,在目标和观察者之间增加一个调度中心。订阅者(观察者)把自己想要订阅的事件注册到调度中心,当该事件触发的时候,发布者(目标)发布该事件到调度中心,由调度中心统一调度订阅者注册到调度中心的处理代码。
1.4 Promise
Promise 是异步编程的一种解决方案,也是一个构造函数,是为解决回调函数地狱这个问题而提出的,它不是新的语法功能,而是一种新的写法,允许将回调函数的嵌套改为链式调用。
- 优点:将回调函数的嵌套改为了链式调用;使用then方法以后,异步任务的两端执行看的更加清楚。
- 缺点:
- Promise 的最大问题是代码冗余,原来的任务被 Promise 包装了一下,不管什么操作,一眼看去都是一堆then,原来的语义变得很不清楚。
- 无法取消Promise,一旦新建它就会立即执行,无法中途取消。
- 如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。
- 当处于Pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。
// Promise 构造函数
let p=new Promise(function(resolve,reject){
if(true){
resolve('承诺兑现了')
}else{
reject('承诺没兑现')
}
}); //p=>承诺对象
console.log(p)
function getRandom(){
return new Promise(function(resolve,reject){
let Random=Math.random() //获取随机数 0-1
if(Random>0.5){
resolve('你成功了:'+Random)
}else{
reject('你失败了:'+Random)
}
})
}
let r=getRandom()//获取承诺对象
console.log(r)
1.5 Generator
Generator 函数是ES6提供的一种异步编程解决方案,语法行为与传统函数完全不同。其最大特点是可以控制函数的执行。Generator函数是将函数分步骤阻塞 ,只有主动调用next() 才能进行下一步
- Generator 函数有多种理解角度。语法上,首先可以把它理解成,Generator 函数是一个状态机,封装了多个内部状态。
- 执行 Generator 函数会返回一个遍历器对象(迭代对象),也就是说,Generator 函数除了状态机,还是一个遍历器对象生成函数。返回的遍历器对象,可以依次遍历 Generator 函数内部的每一个状态。
- 形式上,Generator 函数是一个普通函数,但是有两个特征。一是,
function
关键字与函数名之间有一个星号;二是,函数体内部使用yield
表达式,定义不同的内部状态(yield
在英语里的意思就是“产出”)。- 可暂停函数, yield可暂停,next方法可启动,每次返回的是yield后的表达式结果。
- yield表达式本身没有返回值,或者说总是返回undefined。next方法可以带一个参数,该参数就会被当作上一个yield表达式的返回值。
- 优点:异步操作表示的很简洁,此外可以控制函数的执行。
- 缺点:流程管理不方便,不能实现自动化的流程管理。
function* genF() {
yield 'come on!';
yield 'Front End Engineer';
return 'goood';
}
const gF = genF();
gF.next();// {value: "come on!", done: false}
gF.next();// {value: "Front End Engineer", done: false}
gF.next();// {value: "goood", done: true}
gF.next();// {value: undefined, done: true}
Generator函数的调用方法与普通函数一样,也是在函数名后面加上一对圆括号。不同的是,调用Generator函数后,该函数并不执行,返回的也不是函数运行结果,而是一个指向内部状态的指针对象(迭代器对象),我们可以通过调用 next 方法,使得指针移向下一个状态
1.6 async/await
ES2017 标准引入了async函数,使得异步操作变得更加方便。简言之,该函数就是Generator函数的语法糖。 async/await 函数的实现,就是将 Generator 函数和自动执行器,包装在一个函数里。async函数中的await接收一个Promise对象。
async 函数就是 Generator 函数的语法糖。使得异步操作变得更加⽅便。 async 函数就 是将 Generator 函数的星号( * )替换成 async ,将 yield 替换成 await ,仅此⽽已。 异步函数的运⾏结果为Promise对象,异步函数内部的返回值会作为 then⽅法回调函数的参数。(不需要使用.then方法,不需要使用回调函数)
- 优点:内置执行器,可以自动执行;语义相比Generator更加清晰;返回值是Promise,比Generator函数的返回值是Iterator对象操作更加方便。
- 缺点:增加学习成本。
// 异步函数同步化
async function gen(){
let r=await axios.get(baseURL+'/index/article/pageQuery?page=1&pageSize=10');
console.log(r);
}
//await 命令后⾯是⼀个 Promise 对象,返回该对象的结果。如果不是 Promise 对象,就直接返回对应的值。
gen()
async、await将异步强行转换为同步处理。
async 函数返回的是一个 Promise 对象。
wait 可以用于等待一个 async 函数的返回值——这也可以说是 await 在等 async 函数,但要清楚,它等的实际是一个返回值。注意到 await 不仅仅用于等 Promise 对象,它可以等任意表达式的结果,所以,await 后面实际是可以接普通函数调用或者直接量的。
async 会将其后的函数(函数表达式或 Lambda)的返回值封装成一个 Promise 对象,而 await 会等待这个 Promise 完成,并将其 resolve 的结果返回出来。
async/await 的优势在于处理 then 链。
1、Async—声明一个异步函数(async function someName(){...})
- 自动将常规函数转换成Promise,返回值也是一个Promise对象
- 只有async函数内部的异步操作执行完,才会执行then方法指定的回调函数
- 异步函数内部可以使用await
2、Await—暂停异步的功能执行(var result = await someAsyncCall();)
- 放置在Promise调用之前,await强制其他代码等待,直到Promise完成并返回结果
- 只能与Promise一起使用,不适用与回调
- 只能在async函数内部使用
同步方法:调用时需要等待返回结果,才可以继续往下执行业务
异步方法:调用时无须等待返回结果,可以继续往下执行业务,不会产生阻塞。
await的作用是等待异步Task完成,且不是阻塞的。产生异步函数同步化效果。