JavaScript:Promise让多个异步代码可以按照指定顺序执行

提出问题

使用 ajax 时,会遇到这种情况,需要先发 一个ajax请求,称之为 A请求,返回结果后,再发一个ajax请求,称之为 B请求。

也就是说这2个请求是有先后顺序的

问题:由于ajax是异步调用,也就是说无法确定这2个ajax的请求顺序

这里为了简单,通过 setTimeout() 模拟异步

setTimeout(function(){
     console.log(111) // 虽然写在前面,但是后执行
},1000)

setTimeout(function(){
     console.log(222) // 虽然写在后面,但是先执行
},500)

执行结果:

222
111

解决(正确但不推荐)

嵌套写法,将后面执行的 setTimeout() 放到 前面的 setTimeout 中:

setTimeout(function(){
     console.log(111) 

     setTimeout(function(){
         console.log(222) 
    },500)
},1000)

缺点:如果有多个 setTimeout,就需要多层嵌套,代码不清晰

Promise

Promise 类可以解决上面的问题,可以让异步程序,有顺序的执行

浏览器支持:由于 Promise 是 ES6 新增加的,所以一些旧的浏览器并不支持,苹果的 Safari 10 和 Windows 的 Edge 14 版本以上浏览器才开始支持 ES6 特性。

使用方法

new Promise(function (resolve, reject) {
    // 异步代码
    .......
    // 表示执行成功,接下来会执行 then() 部分
    // 此处的 a,传入 then() 中的 data
    resolve(a) 
    // 表示执行失败,接下来会执行 catch() 部分
    // 此处的 b,传入 catch() 中的 data
    reject(b) 
}).then(function(data){
    // 代码块2
}).catch(function(data){
    // 代码块3
})

需要向 Promise(f) 传入一个函数,该函数有2个参数,这2个参数也是函数

  • 第一个是 resolve(data)(可以没有该代码块) ,调用该函数表示成功,接下来会执行 then() 中的回调函数。
    resolve(data)中的 data 传给 then() 函数

  • 第二个是 reject(data) (可以没有该代码块) ,调用该函数表示失败,接下来会执行 catch() 中的回调函数
    reject(data)中的 data 传给 then() 函数

案例1

实现本文开头两个 setTimeout() 同步写法,即:只需要处理 resolve(data) 成功情况

方式1-基础写法

适合简单功能

new Promise(function(resolve){
    setTimeout(function(){
        console.log(111)
        resolve('AAA') // AAA会传给 then 中的 data
    },1000)

})
.then(function(data){
    setTimeout(function(){
        console.log(222,data) 
    },500)
})

方式2-高级写法

封装成2个函数:

  1. 第一个函数返回 Promise 对象
  2. 第二个函数获取到 Promise 对象后,处理 then 部分

好处:这种方式在 ajax 中经常用到:

  1. 第一个函数封装发送 ajax 请求,是公共函数,不再改变
  2. 第二个函数处理 ajax 请求返回的结果,由用户自定义
function t(){
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log(111)
            resolve('AAA') // AAA会传给 then 中的 data
        },1000);
    });
}
function my(){
    t().then(function(data){
        setTimeout(function(){
            console.log(222,data) 
        },500)
    })
}

my()

案例2

需要处理 resolve(data) 成功情况、reject(data) 失败情况

var successFlag = true
var pro = new Promise(function(resolve, reject){
    setTimeout(function(){
        console.log(111)
        if (successFlag){
            resolve("执行成功!")
        }else{
            reject("执行失败!")
        }

    },1000)

})
.then(function(data){
    setTimeout(function(){
        console.log(222,data) 
    },500)
}).catch(function(data){
    setTimeout(function(){
        console.log(333,data) 
    },500)
})

链式调用then(多个then写法)

then() 支持链式调用,即:支持多个 then() 顺序执行,此时 then() 方法要 返回 一个 Promise 对象

例子

有下面代码,3个函数中,每个函数都返回 Promise,该如何实现:先执行 t(),再实现 t2(),最后执行 t3()

function t(){
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log(111)
            resolve('AAA') // AAA会传给 then 中的 data
        },1000);
    });
}
function t2(){
    return new Promise((resolve, reject) => {
        setTimeout(function(){
            console.log(222) 
            resolve('BBB')
        },700)
    });
}
function t3(){
    return new Promise((resolve, reject) => {
        setTimeout(function(){
            console.log(333) 
            resolve('CCC')
        },500)
    });
}

错误执行方式

下面执行方式是无法保证顺序执行的:

t()
t2()
t3()

正确但不推荐的写法

下面方式执行正确,但又发生嵌套了,不推荐

t()
.then((val)=>{
    console.log("t2-val:",val)
    t2()
    .then((val)=>{
        console.log("t3-val:",val)
        t3()
    })
})

链式调用 then (推荐)

t()
.then((val)=>{
    console.log("t2-val:",val)
    return t2() // then中必须返回Promise
})
.then((val)=>{
    console.log("t3-val:",val)
    return t3() // 最后一个是否返回Promise无所谓
})

参考:
https://juejin.cn/post/6876686095954903048
https://juejin.cn/post/6948401178975666190


原文出处:/show_1IX3oXyazfeK.html