Promise的详细用法解析

650 阅读5分钟

一、Promise是什么及相关需求

  • Promise是ES6新增的内置类(new Promise)
  • Promise是一个“承诺”设计模式,主要目的是用来解决JS异步编程当中“回调地狱”问题(有效的管控异步编程)
  • Promise是用来管控异步编程的,new Promise本身不是异步的,执行它的时候会立即把executor函数执行(只不过我们经常在executor中管控一个异步操作)
    1. resolve / reject:传递给executor函数的参数(参数值都是函数)
    2. promise的初始状态是pending,初始值是undefined
    3. resolve([value]):修改promise的状态为fulfilled/resolved的成功状态,并且改变其值为[value]
    4. reject([reason]):修改promise的状态为rejected,并且改变其值为[reason]
  • 一旦状态发生改变,都不能改变为其他状态了
  • 一旦executor函数执行报错,状态也会变为失败态,并且改变其值是失败的原因
  • promise中的异步指的是resolve/reject执行,原因:
    1. 执行这两个方法的时候不仅仅是修改状态和值,还要通知then存储的两个回调函数中的一个执行
    2. 执行两个方法的时候,需要先等待promise已经基于then把方法存储完毕,有方法后才会去执行
new Promise():TypeError: Promise resolver undefined is not a function
new Promise([executor]): executor 是个可执行的函数
【实例】
 - [[PromiseStatus]] Promise状态:pending、fulfilled、rejected
 - [[PromiseValue]]  Promise的值
【原型】
 - then
 - catch
 - finally
【普通对象】
 - reject
 - resolve
 - all
 - race
  • 基于.then注册成功或失败执行的回调函数,它的返回结果是一个新的promise实例
    1. new Promise([executor])返回的实例的状态和value是根据resolve/reject执行,再或者[executor]函数执行是否报错来决定的
    2. .then(...)返回的实例的状态和value是根据.then注册的两个方法,不论哪一个方法执行,执行的返回结果和是否报错来决定状态value => 不论哪一个方法执行,只要不报错状态是成功,报错状态则为失败,方法的返回值就是新实例的value值;特殊:如果返回的是一个新的promise实例,则当前实例的状态和value决定了p2的的状态和value
    let p1 = new Promise((sesolve, reject) => {
        resolve('OK')
    })
    let p2 = p1.then(value=>{
        console.log('成功执行', value)
    }, reason=> {
        console.log('失败执行', reason)
    })
    p2.then(value=>{
        console.log('成功执行2', value)
    }, reason=> {
        console.log('失败执行2', reason)
    })
    
    

二、then

  1. 基于then存放两个回调函数:
  • 状态为成功调用第一个回调函数执行,形参的值就是[[PromiseValue]]
  • 状态为失败调用第二个回调函数执行
let p = new Promise(resolve, reject) {
    resolve('OK')
    reject('NO')
}
p.then((value)=>{
    console.log('成功执行的函数', value)
}, ()=>{
    console.log('失败执行的函数', value)
})

2.then注入回调方法的时候,我们可以写也可以不写
.then([fnOK], [fnNo])
.then([fnOK])
.then(null, [fnNo])
.then()
如果状态一旦确定,想去执行.then注入的某个方法,但是此方法没有被注册,则向下顺延(找下一个then中注册的对应方法)

Promise.reject(100)
    .then(value=>{
        console.log('OK', value);
    }/*,reason=>{
        return Promise.reject(reason);
    }*/)
    .then(null/*value=>{
        return Promise.resolve(value);
    }*/, reason=>{
        console.log('NO', reason); //NO,100
        return Promise resolve(200);
    })
    .then(null, reason=>{
        console.log('NO', reason);
    })
    .then(value=>{
        console.log('OK', value); //OK,200
    })

三、catch(reason=>{}) === .then(null, reason=>{})

Promise.reject(100).then(value=>{
    console.log('OK', value);
}).catch(reason=>{
    console.log('NO', reason);
})

四、all / race => 返回一个新的Promise实例

all:等待所有Promise实例都是成功,整体返回的实例才是成功(都成功整体才是成功,有一个失败,整体是失败) race:等待最新有返回结果的Promise实例,此实例的成功和失败决定最后的成功和失败

function fn1() {
    return Promise.resolve(1)
}
function fn2() {
    return new Promise(resolve, reject) => {
        setTimeout(()=>{
            resolve(2)
        },2000)
    }
}
function fn3() {
    return new Promise(resolve, reject) => {
        setTimeout(()=>{
            resolve(3)
        },1000)
    }
}

//all: 整体返回的实例才是成功
Promise.all([fn1(),fn2(),fn3()]).then(values=>{
    //values[Array]:按照顺序存储每一个实例返回的结果
    console.log(values);
}).catch(reason=>{
    //一旦有失败的,整体都是失败,存储的是当前这个实例失败的原因
    console.log('NO', reason);
})

//race: 以最先到的为准
Promise.race([fn1(),fn2(),fn3()]).then(value=>{
    console.log('OK',value);
}).catch(reason=>{
    console.log('NO', reason);
})

四、面试题:设计一个等待函数,等待N时间后执行要做的事情

function delay( callback,interval = 1000){
    return new Promise( resolve =>{
        let timer = setTimeout(()=>{
            clearTimeout(timer);
            timer = null;
            resolve();
        },interval);
    });
}

//第一种
delay(1000).then(()=>{
    console.log(1);
    return delay(2000);
}).then(()=>{
    console.log(2);
    return delay(3000);
}).then(()=>{
    console.log(3);
})

//第二种
(async function(){
    await delay(1000);
    console.log(1);
  
    await delay(2000);
    console.log(2);
  
    await delay(3000);
    console.log(3);
})();

五、async、await

是ES7中提供的,它是对promise的一个补充(promise语法糖)
用async修饰一个函数,函数返回的结果都会变成一个promise实例

  • 状态:大多数都是成功的,如果代码执行报错,返回失败,或者手动返回一个新的promise实例,则按照新实例的状态处理 用await可以把一个异步任务变为类似于同步的效果(本质不是同步,还是异步,而且是异步中的微任务)
function fn2() {
    return new Promise(resolve, reject) => {
        setTimeout(()=>{
            resolve(2)
        },2000)
    }
}
function fn3() {
    return new Promise(resolve, reject) => {
        setTimeout(()=>{
            resolve(3)
        },1000)
    }
}

(async function(){
    //方法中想用await,必须把方法基于async修饰
    /*
        1.先把fn2执行,观察fn2返回的是成功还是失败的promise
        2.异步性:它会把当前上下文,await下面的代码整体都当做一个异步的微任务,放置在等待的EventQueue中
        3.await只是处理promise实例是成功状态的,如果返回状态是成功,则value拿到的是[[PromiseValue]],并且把之前存储的异步任务拿到栈中让主线程把其执行
        4.await处理后,立即把函数执行,哪怕函数立即返回成功或者失败的状态,await也没有把其立即处理,而是先等同步的都执行完, 再去执行这些异步的任务
    */
    let value = await fn2();
    console.log(value)
})();
console.log(1)

六、微任务、宏任务练习题

async function async1 (){
    console.log('async1 start');
    await async2();
    console.log('async1 end');
}
async function async2() {
    console.log('async2');
}
console.log('script start');
setTimeout(function(){
    console.log('setTimeout')
},0)
async1();
new Promise(function(resolve){
    console.log('promise1');
    resolve();
}).then(function(){
    console.log('promise2');
});
console.log('script end');