关于Promise那些事儿

334 阅读3分钟

前言

Promise是一种处理异步操作的对象。

异步

  • js是单线程执行的,一次只干一件事。
  • 遇到需要耗时的代码,那就先挂起,先执行不耗时的代码,等到不耗时的代码执行完了,V8腾出手了,再执行耗时的代码。
function foo() {
    console.log("foo");
}
function bar() {
    console.log("bar");
}

foo();
bar();

打印结果

image.png

分析

bar()先被调用,先执行。

如果在foo中放入一个定时器,foo就会v8识别为耗时代码,后执行。

function foo() {
    setTimeout(() => {
        console.log("foo");
    })
}
function bar() {
    console.log("bar");
}

foo();
bar();

打印结果

image.png

如果一个函数b依赖另一个函数a的结果,但是a函数是一个耗时代码,会怎么执行?

let data = {}
function a() {
    setTimeout(() => {  // ajax
        data = {name: 'lxp'}
    }, 1000)
}

function b() {
    console.log(data.name + '超级有钱');
}

a()
b()

打印结果

image.png

分析

由于a是耗时代码所以后执行,所以会先执行b,b打印的值为一个空对象的name,所以为undefined。

把b放到a里去调用

let data = null
function a() {
    setTimeout(() => {  // ajax
        data = {name: 'lxp'}
        b()
    }, 1000)
}

function b() {
    console.log(data.name + '超级有钱');
}

a()

打印结果

image.png

分析

这样我们就能拿到想要的结果,这样写也叫回调

回调函数

将一个函数作为参数传给另一个函数,以便在某些事件发生时,被调用的函数能够在合适的时机执行传递过来的函数。回调函数能够处理异步操作。如果嵌套过深,一旦出现问题很难排查。

回调地狱

function a() {
    b()
}
function b() {
    c()
}
function c() {
    d()
}
function d() {
    e()
}
function e() {}

a()

Promise

能够把一个异步问题很好地执行为一个同步问题。

function eat() {
    return new Promise((resolve, reject) => {
        setTimeout(() =>{
            console.log('吃饭')
            resolve()
        }, 2000) 
    })
}

function sleep() {
    setTimeout(() =>{
        console.log('睡觉')
    }, 2000)
}
function learn() {
    console.log('学习')
}
eat().then(() =>{
    sleep()
    learn()
})

打印结果

image.png

分析

  • eat()返回一个promise对象,这个函数在2s后通过调用resolve()函数来表明异步操作已完成,
  • resolve()是promise内部提供的一个函数,resolve()执行成功后,.then()方法所包含的回调函数就会被调用。
  • 但由于sleep()函数内部没有返回一个promise对象,所以v8引擎不会等待sleep()函数内的定时器执行完毕,直接执行后续learn()的代码。

如果改为:

function eat() {
    return new Promise((resolve, reject) => {
        setTimeout(() =>{
            console.log('吃饭')
            resolve()
        }, 2000) 
    })
}

function sleep() {
    return new Promise((resolve, reject) => {
    setTimeout(() =>{
        console.log('睡觉')
        resolve()
    }, 2000)
})
}
function learn() {
    console.log('学习')
}
eat()
.then(() =>{
     sleep()
})
.then(() =>{
    learn()
})

打印结果

image.png

分析

由于第一个.then()没有任何返回值,所以无法确认是否要等到sleep()执行结束后再执行下一个.then(),所以不会等sleep()执行完毕就直接触发了下一个.then(),就先执行了learn()函数。

将sleep()返回给.then()

eat()
.then(() =>{
    return sleep()
})
.then(() =>{
    learn()
})

打印结果

image.png

reject-catch

定义一个a函数,内部放有一个定时器,在定时器内部放了一个reject()函数,传入err。定义一个函数b,只有当a执行完毕后才执行b,执行失败则返回err。

function a() {
    return new Promise(function(resolve, reject){
        setTimeout(function() {
            console.log('a is ok');
            reject('a')
        }, 1000)
    })
}

function b() {
    console.log('b is ok');
}
// a().then(b())

a()
.then((res) => {
    b()
})
.catch((err) => {
    console.log(err)
})

执行结果

image.png

Promise.race

当需要处理多个异步操作时,若只需要最快执行的异步操作的结果时可以使用Promise.race。

function a() {
    return new Promise(function(resolve, reject){
        setTimeout(function() {
            console.log('a is ok');
            resolve('a')
        }, 1000)
    })
}

function b() {
    return new Promise(function(resolve, reject){
        setTimeout(function() {
            console.log('b is ok');
            resolve('b')
        }, 500)
    })
}

function c() {
    console.log('c is ok');
}

Promise.race([a(), b()]).then(() =>{
    c()
})

打印结果

image.png

Promise.all

用于执行多个异步操作时,当异步操作都成功完成后才执行其他操作。

Promise.all([a(), b()]).then(() =>{
    c()
})

打印结果

image.png

总结

灵活运用Promise,能够更好地处理异步操作。

Thanks1.png