您的位置:首页 > Web前端 > JavaScript

JS中的Promise和async/await

2019-04-28 21:50 519 查看
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_36754767/article/details/89646766

前言

JS执行是单线程的,但是在JS中需要有大量进行查询、获取数据的操作,例如AJAX,如果都按照顺序执行,那么在用户体验等多个方面肯定是极差的。那么就衍生了一系列的异步操作。

callback

最简单,最早产生的异步解决方案就是

callback
,常说的
回调函数
,举个例子:

var A=function(){
//    var aa= fs.readFileSync('test.txt',{encoding:'utf-8'})
// console.log(aa)
for(var i=0;i<999;i++){}
console.log('A')
}
var B=function(callback){
callback()
console.log('B')

}

B(A)

执行后结果如下:
A
B
这个感觉不对劲,不是说

callback
是异步吗,为什么还是顺序执行呢。其实callback函数仍然在主线程中执行,所以,调用callback,当前线程还是会去执行callback。网上有的解释并没有解释清楚。那
callback
异步操作
到底有什么关系呢?看下边个例子:

console.log('A')
$('button').on('click',function(){
console.log('success')
})
console.log('B')

执行结果如下:
A
B
为什么success没有被打印出来呢,应该学过

JS
都知道把,因为它的打印是有条件的,需要点击的时候才能触发,总之就是可以通过这个条件来控制回调函数的执行顺序,现在可能有人还是会不太理解。

JS异步编程原理与回调函数

ES6之前,JavaScript中异步编程分为3类:DOM事件(如onclick)、网络请求(如ajax)、定时器(setTimeout/setInterval)。他们均使用回调函数来进行异步调用。

JS执行栈是单线程,但是JS的宿主环境不是单线程的(如浏览器、Node),浏览器内部是允许多个线程同时运行的,除JavaScript引擎线程外,还有事件触发线程、HTTP请求线程、定时器触发线程,他们和JavaScript线程是互不影响的,不会造成阻塞,JS执行的线程(即JavaScript引擎的所在线程)为主线程,浏览器会执行一个类似于while的轮询过程,每次循环查看线程中是否有待处理的任务,(如浏览器点击事件、AJAX请求、定时器等),如果存在则把该任务添加到JS主线程的执行任务列表中等待执行。

那么我们就很好明白了,诸如事件请求、定时器、事件点击等确实是异步的,但是不是由于

JavaScript
本身的作用,
JavaScript
本身依旧是单线程的,当碰到请求等事件时,浏览器会新开一个线程,当回调函数
callback
调用的时候,那么之前的异步事件就产生了变更把回调函数放到
JavaScript
引擎任务队列中等待执行。

尽管回调函数是一种异步解决方案,但正是异步操作的问题,会产生回调地狱的问题,如下

function async(){
setTimeout(function(){  //回调函数1
console.log(1);
setTimeout(function(){  //回调函数2
console.log(2);
setTimeout(function(){  //回调函数2
console.log(3);
},1000);
},1000);
},1000)
}
async();

//调用结果:1s后打印1 2s后打印2 3s后打印3

这是网上随便找的一个例子,我们可以看到一大堆的 {}(),看完脑袋有点疼,一旦嵌套层级变多,代码结构就变得很不乐观。

Promise


promise

ES6
中提出的一种异步解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现,
ES6
将其写进了语言标准,统一了用法,原生提供了
Promise
对象。其只有then这一个方法,
then
方法带有两个参数:

  • 成功回调
  • 失败回调

promise对象有三个状态:

  • pending(进行中,未完成)
  • resolved(已完成)
  • rejected(已失败)

(1)我们将上边的callback回调函数包装成

Promise

function async1(data){
console.log(data)
var p=new Promise(function(resolve,reject){
setTimeout(function(){

resolve(2)
},1000)

})
return p
}
function async2(data){
console.log(data)
var p=new Promise(function(resolve,reject){
setTimeout(function(){

resolve(3)
},1000)

})
return p
}

(2)使用

then
链式调用这三个方法

async()
.then(function(data){
return async1(data)
})
.then(function(data){
return async2(data)
})
.then(function(data){
console.log(data)
})

(3)还可以简化为

async()
.then((data)=> async1(data))
.then((data)=> async2(data))
.then((data)=>console.log(data))
//调用结果:1s后打印1 2s后打印2 3s后打印3

我们可以看到异步操作的结构清晰了许多,当然我们可以通过

rejected
对异步操作进行错误,我们对函数进行错误处理。

function async(){
var p=new Promise(function(resolve,reject){
setTimeout(function(){

resolve(1)

},1000)

})
return p
}

function async1(data){
console.log(data)
var p=new Promise(function(resolve,reject){
setTimeout(function(){

reject(2)
},1000)

})
return p
}
function async2(data){
console.log(data)
var p=new Promise(function(resolve,reject){
setTimeout(function(){

resolve(3)

},1000)

})
return p
}

执行结果

1
(node:17952) UnhandledPromiseRejectionWarning: 2
(node:17952) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
(node:17952) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

报错了,大概意思就是没对错误进行处理,使用

catch
方法捕捉错误。

function async(){
var p=new Promise(function(resolve,reject){
setTimeout(function(){

reject('这是错误1')

},1000)

})
return p
}

function async1(data){

console.log('这是正确回调')
}

async()
.then(async1,(e)=>{console.log(e)})
//打印结果 这是错误1

我们还可以用

catch
方法来处理
rejected
回调

async()
.then(async1)
.catch((data)=>console.log(data))

catch
方法的另一个作用是如果执行
resolve
时代码出错(抛出错误),不会卡死出错,会进到
catch
函数里边

await和async


await
字面意思就是
等待
,在
JavaScript
中意思和这个差不多,
async
是异步操作,而
await
就是
等待
一个异步任务完成的结果。简单的说
await
是一个操作符,
async
是异步的方法。

await
只能用在
async

async  function demo(){
const data1= await async()
const data2=await async1()
console.log(data1,data2)
}

demo()
//打印结果 1 2

可以看到结果1和结果2同时打印,那么

await
操作符的作用就比较明显了,
await
就是将异步
Promise
同步化的一种解决方案。不需要在
then
链中进行回调操作。

总结

  1. JavaScript
    本身仍然是单线程的,没有异步操作一说,但是其
    宿主环境
    是多线程的问题,造成可以进行一些异步操作。
  2. callback
    本身也不是异步操作的但是结合一些浏览器事件HTTP请求等造成了callback回调异步操作的问题。
  3. callback
    回调函数会造成地狱回调的问题,代码结构不清晰,
    Promise
    对象很好的解决课这个问题。
  4. await
    是操作符,是async同步化的一种方法。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: