promise和async/await的区别

两者都是做异步处理的, 使异步转为同步,目的都是为了解决异步回调产生的“回调地狱”。
同步: 顺序执行,始终和前文保持在一个上下文,可以快速捕获及处理异常。由于js是单线程,当代码量多时容易造成阻塞,耗费时间。
异步: 由浏览器(多线程)提供,解决阻塞,异步任务始终在同步任务全部执行完毕后才执行,像回调函数,ajax、setTimeout。提高了执行效率,节省时间,但是会占用更多资源,不利于对进程控制。
btw… 异步任务其实是可以和同步任务同时进行的,但是异步任务的回调函数(真正的异步)一定是在同步任务全部执行完毕后执行的,因为异步回调任务一定是在js线程中完成的。即异步操作在浏览器线程,而异步执行回到js线程。
回调地狱: 当许多功能连续调用,回调函数层层嵌套,形成一个三角形,造成可读性差,代码可维护性可迭代性差,可扩展性差。

一、什么是promise(承诺)
Promise是ES6中的一个内置对象,(实际是一个构造函数,通过函数可以创建一个promise对象),作用为解决异步调用问题
特点:
①、三种状态:pending(进行中)、resolved(已完成)、rejected(已失败)。只有异步操作的结果可以决定当前是哪一种状态,任何其他操作都不能改变这个状态。

②、两种状态的转化:其一,从pending(进行中)到resolved(已完成)。其二,从pending(进行中)到rejected(已失败)。只有这两种形式的转变。

③、缺点:在pending阶段,无法取消状态,且无法判断pending的进度(是刚开始还是即将结束)

二、使用promise
若需要使用带setTimeout()。其在Chrome中可以传多个参数

setTimeout(function (a, b) {
    console.log(a + b);
}, 1000, 20, 50);  // 70

 

创建一个promise实例:

var mypromise = new Promise( ( resolve,reject )=>{  //构造函数new创建对象,函数必须有 
    // do something...
    If( /*异步操作成功*/ ){
        resolve(value);  // pending---resolve
    }else{
        reject(error);  //pending---reject
    }
} )
//  后续操作:执行回调函数.then()方法来操作成功或失败要操作的事情
mypromise .then( (value)=>{
    // success
}, (error)=>{
    // failure
} )

箭头函数只是初始化promise对象,并且接受了resolve和reject两个函数作为参数。注意if语句不是必要的,只是要表达若异步成功,就调用resolve函数,状态转化,并将value值传递下去,因为promise还有一个.then()方法,即成功(或失败)后的一些操作,这也是resolve和reject内置函数存在的意义。反之若异步失败亦是。

举例: 使用构造函数创建对象

let p = new Promise(function(resolve, reject) { 
// Promise() 就是一个构造函数,通过new创建了p对象实例
    console.log('i am promise');
	var a = 2,b = 1;
    if(a>b){
		resolve(a);
    }else{
		reject(error)
	}
});
p.then(function(value) {  // 执行异步回调函数
	console.log('resolved.');
},function(error){
	console.log('reject')
});
console.log('Hi!');
//  i am promise;   Hi;   resolved;   //可以看出promise是异步的

 

三、promise.prototype.then()

then()作用:是为promise实例添加状态发生改变时的回调函数。第一个参数(必选)是resolve状态的回调,第二个参数(可选)是reject状态回调的函数
then()方法是promise原型上定义的方法。输出promise.prototype可看到内置对象
then()方法支持链式调用,即上一个方法调用后的结果会传给下一个方法。
等同于:

mypromise.then(function(data) {
	console.log('Contents: ' + data);
}).then( function(error) {
	console.error('出错了', error);
} )

第一个回调函数完成后,会将结果作为参数,传递到下一个回调函数。

四、promise.prototype.catch()
建议then()不要使用第二个参数,而使用catch()

Promise.then( function(data){ // 建议
	//success
} ).catch( function(err){
	//error
} )

因为catch() 方法返回的还是一个promise对象,后续可以继续使用then()进行链式调用。
resolve — 对应then。只有调用了resolve才会触发then方法的回调
reject — 对应catch。只有调用了reject才会触发catch方法的回调

五、promise.all()
作用:将多个promise实例包装成一个新的promise实例
Var arr = [1,2,3]
Var p = promise.all(arr).then(…)
其中数组中1,2,3都是promise对象实例。若不是,就会先调用下面讲到的promise.resolve()方法,将参数先转为实例,再下一步处理。
p的状态(成功或失败再执行then回调) 由数组的所有实例对象决定:
1、只有所有实例对象都是resolve,p的状态才resolve。然后返回值也是数组,传递给p的回调函数;
2、当有一个实例被reject了,p状态就会是reject。此时第一个被reject的实例返回值,会传递给p的回调函数。

举例:

var p= [1,2,3,4,5,6].map( function(i){
	console.log(i);
} )
promise.all(p).then(...).catch(...)
 

通过数组map生成6个实例对象,然后作为参数传递promise.all(),只有当全部为resolve时才会调用then()方法,否则调用catch()方法。

六、promise.race()
同五。也是将多个promise实例包装成一个新的promise实例。状态亦是。
Var p = promise.race( [1,2,3] )

七、promise.resolve()
将现有的对象转化为promise对象。看参数的不同分4种情况…

八、promise.reject()
promise.reject(reason)方法也会返回一个新的promise实例,该实例状态为reject。

九、理解promise
其作用就是如果前面的代码非常耗时,就会阻塞后面要执行的代码,所以有异步操作。
举例:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>testsettimeout</title>
</head>
<body>
    <div class="wrap"></div>
    
    <script>
    let promise = new Promise(function(resolve, reject) {
      console.log('Promise! 我是Promise对象里的函数,我最早出现是因为我要给后面一个耗时的家伙提供数据a');
      var a = 20;
      resolve(a);
    });

    promise.then(function(value) {
      console.log('哈哈哈,我得到数据a了,要开始执行喽。我是一个非常耗时的操作,依赖前面的数据,但是我想快一点执行,这有利于用户体验,所以别把我放在后头啊;我被放在了一个新的线程上,不会阻塞别人的');
      for (var i = 0; i < 1000000; i++) {
      	var li = document.createElement("li");
        document.querySelector(".wrap").appendChild(li);
      }
      console.log("执行完毕");
      
    });

    console.log('Hi! 我是什么都不依赖的程序,但我也想快一点执行,不能委屈我啊');
    </script>
</body>
</html>

// 'Promise! 我是Promise对象里的函数...
// Hi! 我是什么都不依赖的程序...
// 哈哈哈,我得到数据a了,要开始执行喽...
// 执行完毕

  

在new promise里的函数是立即执行函数,执行顺序:
(匿名函数—Promise!…)-----(若还有同步任务 —Hi! …)—紧接着是promise.then回调函数的执行。所以promise.then()函数才是真正的异步执行。

Async await
Async搭配await是ES7提出的,基于promise实现,也是非阻塞的异步转同步,让代码更加语义化,更有可读性。
Await函数不能单独使用(无效),事实上async函数会返回一个promise对象,可以使用then函数添加回调。执行函数时,一旦遇到await函数就会先返回一个promise对象,等到异步操作完成,再去执行后面的语句,若await异步操作出错,就相当于async返回的promise对象被reject了。

async函数在function前面有个async作为标识,意思就是异步函数,里面有个await搭配使用,await是等待的意思,那么它等待什么呢,它后面跟着什么呢?其实它后面可以放任何表达式,不过我们更多的是放一个返回promise 对象的表达式,它等待的是promise 对象的执行完毕,并返回结果。每到await的地方就是程序需要等待执行后面的程序,语义化很强,下面总结一下async函数的特点:
1. 语义化强
2. 里面的await只能在async函数中使用
3. await后面的语句可以是promise对象、数字、字符串等
4. async函数返回的是一个Promsie对象
5. await语句后的Promise对象变成reject状态时,那么整个async函数会中断,后面的程序不会继续执行
基于上面的async的特点,我们会用到异常捕获机制-----try…catch…

举例:Async搭配await发送异步请求
现在写一个函数,让它返回promise 对象,该函数的作用是2s 之后让数值乘以2

// 2s 之后返回双倍的值
function doubleAfter2seconds(num) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(2 * num)
        }, 2000);
    } )
}
// 再写一个async 函数,从而可以使用await 关键字, await 后面放置的就是返回promise对象的一个表达式,所以它后面可以写上 doubleAfter2seconds 函数的调用
async function testResult() {
    let first = await doubleAfter2seconds(30);
    let second = await doubleAfter2seconds(50);
    let third = await doubleAfter2seconds(30);
    console.log(first + second + third);   // 220
}
testResult();  // 调用
console.log('先执行');
 

6秒后,控制台输出220, 我们可以看到,写异步代码就像写同步代码一样了,再也没有回调地狱了。

    1. 代码执行过程:调用testResult函数,然后再里面遇到了await函数,要等待,代码就执行到这,等后面的promise对象执行完毕,拿到promise resolve的值进行返回,然后await才会继续向下执行。
    2. 具体到 我们的代码, 遇到await 之后,代码就暂停执行了, 等待doubleAfter2seconds(30) 执行完毕,doubleAfter2seconds(30) 返回的promise 开始执行,2秒 之后,promise resolve 了, 并返回了值为60, 这时await 才拿到返回值60, 然后赋值给result, 暂停结束,代码继续执行,执行 console.log语句。
    3. Async/await相对来说更简洁,不用写很多的then(),不用匿名函数处理。
      让try/catch可以同时处理同步和异步处理。在promise中就无法处理,要使用.catch()方法,代码冗余。
    4. 这里强调一下等待,当js引擎在等待promise resolve 的时候,它并没有真正的暂停工作,它可以处理其它的一些事情,如果我们在testResult函数的调用后面,console.log 一下,你发现 后面console.log的代码先执行。
posted @ 2020-10-03 10:48  丁如超dd  阅读(2936)  评论(0编辑  收藏  举报