promise的入门理解

一、promise是什么?

promise用于异步计算,将异步操作队列化,按照预期的顺序进行,返回符合预期的结果。

1.1异步操作

JavaScript的执行环境是单线程的,所谓单线程是指JS中负责执行JS代码的线程只有一个,JavaScript为检查表单而生,存在的首要目标就是操作DOM,所以JS中很多操作是异步的。
同步:一次只能执行一个任务,函数调用后需要等到函数执行结束,返回执行结果才能进行下一个任务。
异步:与同步相反,可以一起执行多个任务。

1.2常见异步操作

(1)时间侦听与响应

document.getElementById('start').addEventListener('click',start,false);
      function start(){
         //响应事件,进行相应的操作
      }

 //jQuery 用‘.on()’也是事件侦听
  $('start').on('click',start);

(2)回调函数
回调函数是一个可执行的代码段,以参数的形式传递给其他代码。

 //比较常见的有AJAX
    $.ajax('http://baidu.com',{
      success:function(res){
        //这里就是回调函数了
      }
    });
    //或者在页面加载完毕后回调
     $(function(){
         //这里也是回调函数
     })

浏览器中的JavaScript,异步以操作事件为主,回调主要出现在AJAX和File API,此时问题还不算严重,但是node.js出现之后,对异步的依赖加剧了,node.js主要实现的是无阻赛和高并发。所以异步操作是其保障。

二、Promise简介

其基本结构有:

new Promise(
    //执行器 executor
    function(resolve,reject){
        //一段耗时很长的异步操作
        resolve();//数据处理完成
        reject();//数据处理出错
    }
).then(function A(){
    //成功,下一步
},function B(){
    //失败,做相应处理
})

先初始化Promise实例,实例中传入一个参数,参数是一个函数,称其为执行器(executor),执行器中有两个参数:resolve和reject。可以将需要操作的事件放在执行器中进行执行。当操作执行完成之后,如果数据处理完成,调用resolve()方法,如果数据处理出错,则调用reject()方法。调用之后,将会改变当前Promise实例的状态,状态改变之后就会调用then()函数中对应的处理函数,如果调用的是resolve(),则执行A函数,否则如果调用reject()函数,Promise状态变为rejected,会调用B()函数。

Promise是一个代理对象,他和原先要进行的操作并无关系,只引入一个回调,避免更多的回调。
执行图为:
在这里插入图片描述

2.1Promise的3个状态

  • pending:初始状态
  • fulfilled:操作成功
  • rejected:操作失败
    当Promise的状态改变之后,就会立刻触发.then()中的相应函数处理后续。而且状态改变只有两种情况:
  • 初始状态pending转为fulfilled:表示数据处理成功
  • 初始状态pending转为rejected:表示数据处理失败

2.2Promise定时例子

console.log('here we go');
new Promise(resolve=> {
    setTimeout(()=>{
        resolve('hello');
    },2000);
}).then(value=> {
    console.log(value+'world');
});

输出结果为:

here we go
helloworld

由此可以了解Promise运行的一般步骤。
特殊情况:假如在.then()的函数里面不返回新的Promise(),会怎样?

console.log('here we go');
new Promise(resolve=> {
    setTimeout(()=>{
        resolve('Hello');
    },2000);
}).then(value=> {
    console.log(value);
    console.log('everyOne');
    (function(){
        return new Promise(resolve=> {
            setTimeout(()=>{
                console.log('Mr.Laurence');
                resolve();
            },2000);
        });
    }());
    return false;
}).then(value =>{
    console.log(value +' World');
})

输出结果:

here we go
Hello
everyOne
false World
Mr.Laurence

[Done] exited with code=0 in 4.273 seconds

由结果可以看很出,当.then()函数里面不返回新的Promise时,会直接执行下一个.then()函数,并且也是直到所有代码执行结束之后,进程才结束。

2.3.then()函数

  • .then()接受两个函数作为参数,分别是fulfilled状态下的响应函数和rejected状态下的响应函数。
  • .then()返回一个新的Promise实例,所以他可以链式调用
  • 当前面的Promise状态改变时,.then()根据其最终状态,选择特定的状态响应函数执行。

要注意:

  • 状态响应函数可以返回新的Promise,或其他值
  • 如果返回新的Promise,那么下一级.then()会在新Promise状态改变之后执行
  • 如果返回其他值,则会立即执行下一级.then()。

2.4.then()函数的链式结构

console.log('here we go');
new Promise(resolve=> {
    console.log('step 1');
    setTimeout(()=>{
        resolve();
    },1000);
}).then(value =>{
    return new Promise(resolve=> {
        console.log('step 1-1');
        setTimeout(()=> {
            resolve(110);
        },1000);
    })
}).then(value => {
    console.log('step 1-2');
    return value;
}).then(value => {
    console.log('step 1-3');
    return value;
}).then(value=> {
    console.log(value);
    console.log('step 2');
})

输出结果:

here we go
step 1
step 1-1
step 1-2
step 1-3
110
step 2

2.5Promise错误处理

Promise 会自动捕获内部异常,并交给rejected响应函数处理。
错误处理的两种做法:

  • reject(‘错误信息’).then(null,message => {})
  • throw new Error(‘错误信息’).catch(message=> {})
    推荐使用第二种,更加清晰好读,并且可以捕获前面的错误。
console.log('here we go');
new Promise(rsolve=> {
    setTimeout(() => {
        throw new Error('bye');
    },1000);
}).then(value=> {
    console.log(value);
}).catch(error => {
    console.log('error:'+error.message);
})

输出结果:

here we go
d:\biancheng\java\javascript\JavaScript\timeOut.js:4
        throw new Error('bye');
        ^

Error: bye
    at Timeout.setTimeout [as _onTimeout] (d:\biancheng\java\javascript\JavaScript\timeOut.js:4:15)
    at ontimeout (timers.js:498:11)
    at tryOnTimeout (timers.js:323:5)
    at Timer.listOnTimeout (timers.js:290:5)

如果调用rejected回调函数情况:

new Promise(rsolve=> {
    setTimeout(() => {
        throw new Error('bye');
    },1000);
}).then(value=> {
    console.log(value);
},value => {
    console.log('Error:'+value);
})

输出结果:

d:\biancheng\java\javascript\JavaScript\timeOut.js:4
        throw new Error('bye');
        ^

Error: bye
    at Timeout.setTimeout [as _onTimeout]  (d:\biancheng\java\javascript\JavaScript\timeOut.js:4:15)
    at ontimeout (timers.js:498:11)
    at tryOnTimeout (timers.js:323:5)
    at Timer.listOnTimeout (timers.js:290:5)

catch也将返回一个Promise实例,所以存在catch后面得.then()也将会依次执行。此外推荐在所有队列最后都加上.catch(),以避免漏掉错误处理造成意想不到的问题。

三、Promise进阶

3.1Promise.all()

批量执行一堆Promise的意思。即Promise.all([p1,p2,p3,…])用于将多个Promise实例,包装成一个新的Promise实例。返回一个新的Promise,接收一个数组作为参数,数组可以是Promise对象,也可以是别的值,只有Promise对象会等待状态的改变。
当所有子Promise都完成,该Promise完成,返回值是全部值的数组,
有任何一个失败,该Promise失败,返回值是第一个失败的子Promise的结果。

console.log('here we go');
Promise.all([1,2,3])
.then(all =>{
    console.log('1: '+ all);
    return Promise.all([function(){
        console.log('ooxx');
    },'xxoo',false]);
}).then(all => {
    console.log('2: '+all);
    let p1= new  Promise(resolve => {
        setTimeout(()=>{
            resolve('I\'m p1');
        },1500);
    });
    let p2= new Promise(resolve=> {
        setTimeout(() => {
            resolve('I\'m p2');
        },1450);
    });
    return Promise.all([p1,p2]);
}).then(all => {
    console.log('3: '+all);
    let p1=new Promise(resolve => {
        setTimeout(() => {
            resolve('I\'m p1');
        },1450);
    });
    let p2=new Promise((resolve ,reject)=> {
        setTimeout(() => {
            reject('I\'m p2');
        },1500);
    });
    let p3=new Promise((resolve,reject) => {
        setTimeout(() => {
            reject('I\'m p3');
        },2000);
    });
    return Promise.all([p1,p2,p3]);
}).then(all => {
    console.log('all:' +all);
}).catch(err => {
    console.log('catch:'+ err);
})

输出结果:

here we go
1: 1,2,3
2: function (){
        console.log('ooxx');
    },xxoo,false
3: I'm p1,I'm p2
catch:I'm p2

其最常见的用法是与map结合使用。

3.2Promise.resolve()

返回一个fulfilled的Promise实例或原始Promise实例。

  • 参数为空,返回一个状态为fulfilled的Promise实例,
  • 参数是一个跟Promise无关的值,同上,不过fulfilled响应函数会得到这个参数。
  • 参数为Promise实例,则返回该实例,不做任何修改。
  • 参数是thenable,立刻执行他的.then()函数。
console.log('start');
Promise.resolve()   //参数为空,返回一个状态为fulfilled的Promise实例
.then((value) => {
    console.log('step 1 ',value);
    return Promise.resolve('Hello');//参数是一个跟Promise无关的值,同上,
}).then(value => {
    console.log(value,' World');
    return Promise.resolve(new Promise(resolve=> {
        setTimeout(()=> {
            resolve('Good');
        },2000);
    }));//参数是一个Promise实例,则返回该实例,不做任何修改
}).then(value => {
    console.log(value, ' evening');
    return Promise.resolve({
        then(){
            console.log(' everyone');
        }//参数是thenable则立即执行.then()函数。
    });
})

输出结果:

start
step 1  undefined
Hello  World
Good  evening
 everyone

3.3Promise.reject()

Promise.reject()返回一个rejected状态的Promise实例,与Promise.resolve()不同之处就是Promise.reject()不认thenable

let promise = Promise.reject('something wrong');
promise.then(()=> {
    console.log('it is OK');
}).catch(()=> {
    console.log('no, it is not Ok');
    return Promise.reject({
        then(){
            console.log('it will be Ok');
        },
        catch(){
            console.log('not yet');
        }
    });
});

结果将输出:no, it is not Ok

3.4Promise.race()

Promise.race()类似于Promise.all(),区别在于他有任意一个完成就算完成。不需要等待所有实例都完成。
常见用法是:把异步操作和定时器放在一起,如果定时器先触发,就认为超时,告知用户。

Promise常见的用法就是把回调包装成Promise实例,他有两个显而易见的好处:

  • 可读性更好
  • 返回的结果可以加入任何Promise队列。

结语

本篇文章仅仅是我作为一个刚开始找工作时,刷面经刷到Promise的原理经常会在面试中被问到,所以对其进行了学习总结,例子都来自于慕课网一个老师的视频教程课中,比较简单易懂。但是只适合入门,如果要深入掌握还需要进一步学习。(Meathill的《Promise入门》)

猜你喜欢

转载自blog.csdn.net/qq_34272207/article/details/89259917