题目⑦ js是如何实现异步编程的?
如何实现异步io?
1.回调函数callback
-
优点:解决了同步的问题(只要有一个任务耗时很长,后面的任务都必须排队等待,会拖延整个程序的执行)
-
缺点:回调地狱,不能用
try catch捕获错误,不能return
2.Promise
-
优点:解决了回调地狱的问题
-
缺点:无法取消
Promise,错误需要通过回调函数来捕获
3.async/await
-
优点:代码清晰,不像
Promise写一堆then链,处理了回调地狱的问题 -
缺点:
await将异步代码改造成同步代码,如果多个异步操作没有依赖性而使用await会导致性能降低
3.1async/await如何通过同步的方式实现异步?
async/await是参照generator封装的一套异步处理方案,可以理解为generator的语法糖,而generator又依赖于迭代器Iterator,Iterator的思想又源于单向链表
3.1.1 单向链表
-
链表的优点
-
无需预先分配内存
-
插入/删除节点不影响其他节点,效率高
-
-
单向链表
是链表中最简单的一种,包含两个域:信息域与指针域,这个链接指向列表中的下一个节点,而最后一个节点则指向一个空值。
-
特点:
-
节点的链接方向是单向的
-
相对于数组来说,单链表的随机访问速度较慢,但单链表删除/添加数据的效率很高
-
-
3.1.2 Iterator
- Iterator迭代器 的遍历过程类似于单向链表
3.1.3 Generator
- Generator:生成器对象时生成器函数返回的,它符合可迭代协议和迭代器协议,既是迭代器也是可迭代对象,可以调用 next 方法,但它不是函数,更不是构造函数
本质:暂停
它会让程序执行到指定位置先暂停 (yield),然后再启动(next),再暂停(yield),再启动(next),而这个暂停就很容易让它和异步操作产生联系
-
处理异步时
-
开始异步处理(网络请求、IO操作)
-
然后暂停一下
-
处理完,再该干嘛干嘛
-
-
js是单线程的,异步还是异步,
callback还是callback,不会因为generator而有任何改变
3.1.4 async/await
- async/await是 generator 的语法糖,就是一个自执行的 generate函数。利用 generator函数的特性把异步的代码写成同步的形式
async/await 和promise的用法?
1. async/await的用法
-
async/await是函数定义的关键字 -
await用于等待promise对象的返回结果,且不能单独使用必须放在async函数中 -
利用
async定义的函数会返回一个promise对象 -
async函数的返回值就是promise状态为resolved的返回值
2. promise
promise是一个构造函数,有allrejectresolve这几个方法,原型上有thencatch等方法
2.1 特点
① 对象的状态不受外界影响
-
promise对象代表一个异步操作,有三种状态-
pending(进行中) -
fulfilled(已成功) -
rejected(已失败)
-
② 一旦状态改变就不会再变,任何时候都可以得到这个结果
-
promise对象的状态改变,只有两种可能-
从
pending变为fulfilled -
从
pending变为rejected
-
2.2 promise的用法
① 首先new一个promise
let p = new Promise(function(resolve, reject) {
// do sth async
setTimeout(function() {
console.log('执行完成promise')
resolve('要返回的数据')
}, 2000)
})
// 执行完成promise
② new一个promise对象,不需要调用就执行了
③ 使用promise的时候一般是包在一个函数中,在需要的时候去运行这个函数
const clickFunc = () => {
console.log('点击方法被调用')
return new Promise(function(resolve, reject) {
// do sth async
setTimeout(function() {
console.log('执行完成promise')
resolve('要返回的数据')
}, 2000)
})
}
为什么要放在函数里面
- 函数
return出promise对象,执行这个函数我们可以得到一个promise对象。接下来就可以用promise对象上的thencatch方法了
resolve是什么
promise只是能够简化层层回调的写法,实质上,promise的精髓是 状态,用维护状态、传递状态的方式使得回调函数能够及时调用
reject的用法
then方法可以接受两个参数,第一个对应resolved的回调,第二个对应reject的回调,并且能在回调函数中拿到成功的数据和失败的原因
function promiseClick(){
return new Promise(function(resolve, reject){
setTimeout(function(){
var num = Math.ceil(Math.random()*20); //生成1-10的随机数
console.log('随机数生成的值:',num)
if(num<=10){
resolve(num);
}
else{
reject('数字太于10了即将执行失败回调');
}
}, 2000);
})
}
promiseClick().then(
function(data){
console.log('resolved成功回调');
console.log('成功回调接受的值:',data);
},
function(reason){
console.log('rejected失败回调');
console.log('失败执行回调抛出失败原因:',reason);
}
);
catch的用法
-
作用一:用来捕获异常,与
then方法中接受的第二参数rejected的回调一样 -
作用二:再执行
resolved的回调时,如果抛出异常,并不会报错卡死js,而是会进入catch方法中
all的用法
- 该方法提供了并行执行异步操作的能力,并且在所有异步操作执行完后并且执行结果都是成功的时候才执行回调
function promiseClick1(){
return new Promise(function(resolve, reject){
setTimeout(function(){
var num = Math.ceil(Math.random()*20); //生成1-10的随机数
console.log('随机数生成的值:',num)
if(num<=10){
resolve(num);
}
else{
reject('数字太于10了即将执行失败回调');
}
}, 2000);
})
}
function promiseClick2(){
return new Promise(function(resolve, reject){
setTimeout(function(){
var num = Math.ceil(Math.random()*20); //生成1-10的随机数
console.log('随机数生成的值:',num)
if(num<=10){
resolve(num);
}
else{
reject('数字太于10了即将执行失败回调');
}
}, 2000);
})
}
function promiseClick3(){
return new Promise(function(resolve, reject){
setTimeout(function(){
var num = Math.ceil(Math.random()*20); //生成1-10的随机数
console.log('随机数生成的值:',num)
if(num<=10){
resolve(num);
}
else{
reject('数字太于10了即将执行失败回调');
}
}, 2000);
})
}
Promise
.all([promiseClick3(), promiseClick2(), promiseClick1()])
.then(function(results){
console.log(results);
});
race的用法
-
谁先执行完成就先执行回调
-
先执行完的不管是进行了
race的成功回调还是失败回调,其余的将不会再进入race的任何回调
function promiseClick1(){
return new Promise(function(resolve, reject){
setTimeout(function(){
var num = Math.ceil(Math.random()*20); //生成1-10的随机数
console.log('随机数生成的值:',num)
if(num<=10){
resolve(num);
}
else{
reject('数字太于10了即将执行失败回调');
}
}, 2000);
})
}
function promiseClick2(){
return new Promise(function(resolve, reject){
setTimeout(function(){
var num = Math.ceil(Math.random()*20); //生成1-10的随机数
console.log('随机数生成的值:',num)
if(num<=10){
resolve(num);
}
else{
reject('数字太于10了即将执行失败回调');
}
}, 2000);
})?
}
function promiseClick3(){
return new Promise(function(resolve, reject){
setTimeout(function(){
var num = Math.ceil(Math.random()*20); //生成1-10的随机数
console.log('随机数生成的值:',num)
if(num<=10){
resolve(num);
}
else{
reject('数字太于10了即将执行失败回调');
}
}, 2000);
})
}
Promise
.race([promiseClick3(), promiseClick2(), promiseClick1()])
.then(function(results){
console.log(results);
},function(reason){
console.log('失败',reason);
});
async/await 和promise用法的区别?举例两三个?
1. promise是ES6,async/await是ES7
async await是基于Promise实现的,可以说是改良版的Promise,它不能用于普通的回调函数
2. async/await相对于promise来讲,写法更加简洁
-
Promise的出现解决了传统callback函数导致的“地域回调”问题,但它的语法导致了它向纵向发展行成了一个回调链,遇到复杂的业务场景,这样的语法显然也是不美观的。 -
而
async await代码看起来会简洁些,使得异步代码看起来像同步代码,await的本质是可以提供等同于“同步效果”的等待异步返回能力的语法糖,只有这一句代码执行完,才会执行下一句
3. reject状态:
-
promise错误可以通过catch来捕捉,建议尾部捕获错误, -
async/await既可以用.then又可以用try-catch捕捉
async/await 和promise性能的区别?
async/await优点
-
它做到了真正的串行的同步写法,代码阅读相对容易
-
对于条件语句和其他流程语句比较友好,可以直接写到判断条件里面
-
处理复杂流程时,在代码清晰度方面有优势
async/await缺点
-
无法处理
promise返回的reject对象,要借助try...catch... -
await只能串行,做不到并行await做不到并行,不代表async不能并行,只要await不在同一个async函数里就可以并行
-
try...catch...内部的变量无法传递给下一个try...catch... -
async/await无法简单实现Promise的各种原生方法,比如.race()之类
浙公网安备 33010602011771号