了解Promise

345 阅读3分钟

Promise执行顺序

先上demo:

new Promise(resolve => {
    console.log("Promise");
    resolve();
})
.then(function() {
    new Promise(resolve => {
        console.log("promise1");
        resolve();
    })
    .then(function() {
        console.log("promise1-1");
    })
    .then(function() {
        console.log("promise1-2");
    })
    .then(function() {
        console.log("promise1-3");
    })
    .then(function() {
        console.log("promise1-4");
    });
    console.log("after promise1");
})
.then(function() {
    new Promise(resolve => {
        console.log("promise2");
        resolve();
    })
    .then(function() {
        console.log("promise2-1");
    })
    .then(function() {
        console.log("promise2-2");
    })
    .then(function() {
        console.log("promise2-3");
    })
    .then(function() {
        console.log("promise2-4");
    });
    console.log("after promise2");
})
.then(function() {
    new Promise(resolve => {
        console.log("promise3");
        resolve();
    })
    .then(function() {
        console.log("promise3-1");
    })
    .then(function() {
        console.log("promise3-2");
    })
    .then(function() {
        console.log("promise3-3");
    })
    .then(function() {
        console.log("promise3-4");
    });
    console.log("after promise3");
});
console.log("after promise")

这个demo的执行顺序为

Promise
after promise
promise1
after promise1
promise1-1
promise2
after promise2
promise1-2
promise2-1
promise3
after promise3
promise1-3
promise2-2
promise3-1
promise1-4
promise2-3
promise3-2
promise2-4
promise3-3
promise3-4

为什么会这样呢?

  1. 首先每次遇到then或者catch,即revolve或者reject就会将这个回调放到下一个microTask里去
  2. js的执行顺序是时间栈,即内层的执行顺序先于外层,而我们计算事件的microTask层数可以通过该事件存在与几个then里面或后面,即
new Promise((resolve, reject) => {
    fn(0)
    resolve()
})
.then(() => {
    fn(1)
    new Promise((resolve, reject) => {
        fn(3)
        resolve();
    })
    .then(() => {
        fn(4)
    })
})
.then(() => {
    fn(2)
})

fn(0)为0层,fn(1)fn(3)层数为1层,fn(2)fn(4)为2层,但后于fn(4)在更里面,它是在fn(3)下面的resolve时进入的下一个microTask,因此其执行时间先于fn(2)

如果需要按顺序输出,需要如何修改代码呢?

Promise
promise1
promise1-1
promise1-2
promise1-3
promise1-4
promise2
promise2-1
promise2-2
promise2-3
promise2-4
promise3
promise3-1
promise3-2
promise3-3
promise3-4

答案如下:

new Promise(resolve => {
    console.log("Promise");
    resolve();
})
.then(function() {
    return new Promise(resolve => {
        console.log("promise1");
        resolve();
    })
    .then(function() {
        console.log("promise1-1");
    })
    .then(function() {
        console.log("promise1-2");
    })
    .then(function() {
        console.log("promise1-3");
    })
    .then(function() {
        console.log("promise1-4");
    });
    console.log("after promise1");
})
.then(function() {
    return new Promise(resolve => {
        console.log("promise2");
        resolve();
    })
    .then(function() {
        console.log("promise2-1");
    })
    .then(function() {
        console.log("promise2-2");
    })
    .then(function() {
        console.log("promise2-3");
    })
    .then(function() {
        console.log("promise2-4");
    });
    console.log("after promise2");
})
.then(function() {
    return new Promise(resolve => {
        console.log("promise3");
        resolve();
    })
    .then(function() {
        console.log("promise3-1");
    })
    .then(function() {
        console.log("promise3-2");
    })
    .then(function() {
        console.log("promise3-3");
    })
    .then(function() {
        console.log("promise3-4");
    });
    console.log("after promise3");
});
console.log("after promise")

区别在于在promise1和promise2中有无显式return promise实例。 当显式return之后,就必须等promise1中的链式then执行完毕,才能执行promise2,同理,执行完promise2中的链式then,才能执行primise3。 promise.then方法里需要传递一个函数作为成功回调,然后源码里又一个特殊判断if(res instanceOf Promise) return res.then(onFullfiledNext);意思是,如果这个成功的回调函数返回的是一个promise,那么下一个then注册的成功回调函数,需要在这个promise成功后才会执行。

Promise.all(iterable)

all的用法:都跑完了的话看结束时间执行自己的revolve,有人失败了执行它的 reject

该方法返回一个promise实例,此实例在 iterable 参数内所有的 promiseresolved 或参数中不再包含 promise 时回调完成 resolve ;如果参数中 promise 有一个 rejected ,此实例回调失败 reject ,失败的原因是第一个失败 promise 的结果。

使用场景:需要并行多个异步操作时,并且在一个回调中处理所有的返回数据。 例如多资源加载。

let Promise1 = new Promise(function(resolve, reject){})
let Promise2 = new Promise(function(resolve, reject){})
let Promise3 = new Promise(function(resolve, reject){})

let p = Promise.all([Promise1, Promise2, Promise3])

p.then(funciton(){
  // 三个都成功则成功  
}, function(){
  // 只要有失败,则失败 
})

Promise.race(iterable)

race的用法:谁跑的快,以谁为准执行回调。

该方法返回一个 promise,一旦迭代器中的某个 promise 解决或拒绝,返回的 promise 就会解决或拒绝。

使用场景:定时操作,若目标时间内无返回则。。

 //请求某个图片资源
function requestImg(){
    var p = new Promise((resolve, reject) => {
        var img = new Image();
        img.onload = function(){
            resolve(img);
        }
        img.src = '图片的路径';
    });
    return p;
}
//延时函数,用于给请求计时
function timeout(){
    var p = new Promise((resolve, reject) => {
        setTimeout(() => {
            reject('图片请求超时');
        }, 5000);
    });
    return p;
}
Promise.race([requestImg(), timeout()]).then((data) =>{
    console.log(data);
}).catch((err) => {
    console.log(err);
});

Promise.allSettled

allSettled的用法:等所有人答完题了,统一收卷返回。

该方法返回一个在所有给定的 promiseresolverejectpromise ,并带有一个对象数组,每个对象表示对应的promise结果。

Promise.allSettled([Promise.reject('No way'), Promise.resolve('Here')])
  .then(results => {
    console.log(results);
    // [
    //   {status: "rejected", reason: "No way"},
    //   {status: "fulfilled", value: "Here"}
    // ]
  }, error => {
    // No error can get here!
  })