promise详解

Promise

之前我知道Promise 怎么用,却不知道为什么要用Promise

有的人可以会想,执行异步操作我用async/await 就行了,为什么还要学promise?

因为 带 async 关键字的函数,它使得你的函数的返回值必定是 promise 对象

所以async的原理实现就是用的promise,不学promise可能不会对你的使用有太大影响,但是如果你知道原理,我觉得你可以会用的更加顺手。

Why

前端操作中,经常要有异步操作,比如在等待服务端返回response的时候,异步操作显得格外重要。

看下面的这段代码,并猜测一下运行结果是什么

const value = (() => {
    console.log(1);
    let res = 0;
    setTimeout(() => {
        console.log(2);
        res = 4
    }, 0)
    console.log(3);
    return res;
})()
console.log(value);

如果你的答案是下面中的其中一种:

1 2 3 4
1 3 2 4

那么你就错了.

如果你是第一种,那你就全错了,这时候我建议你去看一下下面这篇文章window.setTimeout - Web API 接口参考 | MDN (mozilla.org),了解一下setTimeout后再阅读下面的部分

上面那段代码的运行结果是这样的

1
3
0
2

你可能会很疑惑?为什么是0?我不是将res设置为4了吗?

一种解释是:console.log(value)res=4先执行,所以res还没有更新

但是如果你的代码是这么写的

const promise = new Promise((resolve, reject) => {
    console.log(1);
    setTimeout(() => {
        console.log(2);
        resolve(4);
    }, 0);
    console.log(3);
})
promise.then(
    (value) => {
        console.log(value);
    },
    (reason) => {
        console.log("error");
    })

这时候运行结果就如你所想的那样

1
3
2
4

用另外一句话说:你的代码运行顺序如你所想的那样

所以通俗一点讲,promise的作用:让你的代码运行顺序和你想的一样

规范一点讲:

  1. 异步操作更加灵活,可以在操作后指定回调函数
  2. 支持链式调用,解决回调地狱

What

1. 一种实现异步操作的解决方案
2. 封装了异步操作并且可以获得其结果
3. 状态有三种:pending,resolved,rejected
  1. pending:等待状态,没有成功,也没有失败
  2. resolved:成功状态
  3. rejected:失败状态

注意点

1. promise有且只有两种状态变化
  1. pending–>resolved
  2. pending–>rejected
2. promise改变状态的方式
  1. resolve():pending->resolved
  2. return: pending->resolved
  3. reject():pending->rejected
  4. throw:pending->rejected
3. 指定多个成功/失败回调函数时,都会执行相应状态变化的函数
const promise = new Promise((resolve, reject) => {
    console.log(1);
    setTimeout(() => {
        console.log(2);
        resolve(4);
    }, 0);
    console.log(3);
})
promise.then((value) => {
    console.log(value);
})
promise.then((value) => {
    console.log(value+1);
})

运行结果是1 3 2 4 5

也就是说,then可以指定两次

4. then()返回的结果由执行的回调函数决定
new Promise((resolve, reject)=>{
    resolve(1);
}).then( // 1
    value=>{
        console.log("onResolved1()",value);
        throw 2;
    },
    reason=>{
        console.log("onRejected1()",reason);
    }
).then( // 2
    value=>{
        console.log("onResolved2()",value);
        throw 2;
    },
    reason=>{
        console.log("onRejected2()",reason);
    }
)

运行结果:

onResolved1() 1
onRejected2() 2

这里出现了两个then连着用,这就是then的链式调用,第二个then的结果由第一个then所执行的回调函数执行结果决定

5. then()指定回调函数,是同步执行,执行回调函数是异步执行,这点要分清楚
6. 在then的回调函数中执行异步任务,需要return new promise

现在有一个需求,我要再then中执行settimeout,也就是执行异步任务,我该怎么做?

你可能会这么写

new Promise((resolve, reject) => {
    resolve(1);
}).then(
    value => {
        console.log("onResolved1()", value);
        let res = 0;
        setTimeout(() => {
            console.log("task 异步");
            res = 3;
        }, 1000);
        return res;
    },
    reason => {
        console.log("onRejected1()", reason);
    }
).then(
    value => {
        console.log("onResolved2()", value);
    },
    reason => {
        console.log("onRejected2()", reason);
    }
)

但是这次代码的执行顺序又和你想的不一样了,上面代码的执行结果是这样的

onResolved1() 1
onResolved2() 0
task 异步

而你期望的执行结果应该是这样

onResolved1() 1
task 异步
onResolved2() 3

又出现了之前的问题?

上一次我们用promise解决了问题,那这次我们还能不能用promise解决问题?

可以可以可以

new Promise((resolve, reject) => {
    resolve(1);
}).then(
    value => {
        console.log("onResolved1()", value);
        // 再return一个promise
        return new Promise((resolve, reject)=>{
            setTimeout(()=>{
                console.log("task 异步");
                resolve(3);
            },1000);
        })
    },
    reason => {
        console.log("onRejected1()", reason);
    }
).then(
    value => {
        console.log("onResolved2()", value);
    },
    reason => {
        console.log("onRejected2()", reason);
    }
)
7. rejected的传递

有时候你会出现这样一种需求:我得在rejecte前面执行很多个resolve,这其中可能会出现错误,我希望错误可以传递下来。

new Promise((resolve, reject) => {
    reject(1);
}).then(
    value => {
        console.log("onResolved1()", value);
        return 2;
    },
    // 可以不写,默认会加
).then(
    value => {
        console.log("onResolved2()", value);
        return 3
    },
    reason => { throw reason }
).then(
    value => {
        console.log("onResolved3()", value);
    },
    // 没有写函数体时,默认加上return
    reason => Promise.reject(reason)
).catch(reason => {
    console.log('onRejected1()',reason);
})

你可以看到你需要的运行结果

onRejected1() 1
8. 中断promise链

有时候,你想要中断你写的那个很长的then链,怎么办?

返回一个pending状态的promise!!!

new Promise((resolve, reject) => {
    reject(1);
}).then(
    value => {
        console.log("onResolved1()", value);
        return 2;
    },
    // 可以不写,默认会加
).then(
    value => {
        console.log("onResolved2()", value);
        return 3
    },
    reason => { throw reason }
).then(
    value => {
        console.log("onResolved3()", value);
    },
    // 没有写函数体时,默认加上return
    reason => Promise.reject(reason)
).then(
    value => {
        console.log("onResolved3()", value);
    },
    // 没有写函数体时,默认加上return
    reason => {return new Promise(()=>{})} // 返回pending状态的promise
).catch(reason => {
    console.log('onRejected1()',reason);
})
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值