promise的那些事

56 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第11天,点击查看活动详情

ES6中的promise

理解以及它的实例方法

// 异步编程的一种解决方案
// 可以解决多个串联的异步操作形成的回调地狱

/**
 * 理解:promise是一个构造函数,用来生成promise实例
 * new Promise()接受一个函数为参数,这个函数有两个参数:
 * resolve -- pending 变成 fulfilled 状态,异步操作成功时候调用;
 * reject -- pending 变成 rejected 状态,异步操作失败时候调用
 */
const p = new Promise(function (resolve, reject) {
    setTimeout(() => {
        const time = new Date().getTime()
        if (time % 2 == 0) {
            resolve('成功状态:success')
        } else {
            reject('失败状态:fail')
        }
    }, 1000);

})

/**
 * promise 实例方法:
 * then()  --- 当实例状态发生改变时候的回调函数,返回一个新的promise实例(也是promise可以链式调用的原因)
 * catch() --- 用于指定发生错误的回调函数,通常用来替代then的第二个参数:reason
 * finally() --- 不管promise的状态成功还是失败都会执行的操作
 */

p.then((value) => { // 成功状态
        console.log(value)
    },
    // (reason) => { // 失败状态
    //     console.log(reason)
    // }
).catch((value) => { // 通常替代then的失败状态
    console.log(value)
}).finally(() => {
    console.log('finally:我都会执行这里的代码')
})

问题来了:promise究竟怎么解决回调地狱的呢?0149753F.png

用promise解决回调地狱

// 例如:登录——>拿到歌单数据——>根据歌单获取歌曲列表

// 定时器模拟(回调地狱,代码一直往右递进,可观性差)
// setTimeout(() => {
//     console.log(111)
//     setTimeout(() => {
//         console.log(222)
//         setTimeout(() => {
//             console.log(333)
//         }, 3000);
//     }, 2000);
// }, 1000);

// promise解决,then的链式调用(代码往下走,逻辑清晰,可观性好,维护性强)
const p = new Promise((resolve, reject) => {
    setTimeout(() => {
        console.log(1111)
        resolve(2222) // then接收的数据
    }, 1000);
})

p.then(value => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log(value)
            resolve(3333)
        }, 2000);
    })
}).then(value => {
    setTimeout(() => {
        console.log(value)
    }, 3000);
})

promise.all应用

// 使用:把多个promise实例包装成一个新的promise实例

let p1 = new Promise((resolve, reject) => {
    resolve('成功1')
})
let p2 = new Promise((resolve, reject) => {
    resolve('成功2')
})
let p3 = new Promise((resolve, reject) => {
    resolve('成功3')
})

// 参数可以不是数组,但必须是iterator接口
let pAll = Promise.all([p1, p2, p3])
console.log(pAll)

/**
 * pAll由 p1, p2, p3 决定;
 * 只有3个都是成功,pAll才会成功; 
 * 有失败的话,pAll就是失败,并返回第一个失败实例的返回值;
 * ☆ 如果作为参数的实例,例如p1, p2, p3,它自己定义了catch方法,一旦rejected,也不会触发pAll的catch方法
 */

pAll.then(value => {
    console.log(value)  // 全成功:['成功1', '成功2', '成功3']; 
}).catch(reason => {
    console.log(reason)  // 第二个失败:成功2;第二第三都是失败:成功2
})

image.png

// 使用场景:多个请求结果合并在一起,等多个结果一起返回再渲染页面,提高用户体验感
function getBanner() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve('轮播图数据')
        }, 1000);
    })
}

function getId() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            reject('用户信息')
        }, 2000);
    })
}

function getSong() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve('歌单列表')
        }, 3000);
    })
}

function initLoad() {
    let all = Promise.all([getBanner(), getId(), getSong()])
    all.then(value => {
        console.log(value) // 3秒之后返回数据[ '轮播图数据', '用户信息', '歌单列表' ]
    }).catch(reason => {
        console.log(reason) // 如果有失败,返回第一个失败的结果
    })
}
initLoad()

promise.race应用

/**
 * 把多个promise包装成一个新的promise;
 * 
 * promise.race --- 第一个改变状态的实例决定race状态,并返回第一个改变状态的实例的返回值;
 */

let p1 = new Promise((resolve, reject) => {
    setTimeout(() => {
        reject('2秒后')
    }, 2000);
})
let p2 = new Promise((resolve, reject) => {
    setTimeout(() => {
        reject('1秒后')
    }, 1000);
})


// 参数可以不是数组,但必须是iterator接口
let pAll = Promise.race([p1, p2])
console.log(pAll)
pAll.then(value => {
    console.log(value)
})

// 使用场景:请求超时提示
function getRequest() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve('请求成功')
        }, 4000);
    })
}

function timeout() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            reject('网络不好,点击重试')
        }, 3000);
    })
}

function init() {
    Promise.race([getRequest(), timeout()])
        .then(value => {
            console.log(value)
        })
        .catch(reason => {
            console.log(reason)
        })
}
init()

使用promise封装axios

function getAjax(url) {
    return new Promise((resolve, reject) => {
        // 创建一个实例对象
        let xhr = new XMLHttpRequest()
        // 新建一个http请求
        xhr.open('GET', url, true)
        // 发送http请求
        xhr.send(null)
        // 设置状态的监听函数
        xhr.onreadystatechange = function () {
            if (xhr.readyState !== 4) return // 表示请求成功才能继续往下执行
            if (xhr.status >= 200 && xhr.status < 300) { // 表示请求成功是否
                // 请求成功,把请求结果返回出去
                resolve(xhr.response)
            } else {
                reject(new Error(this.statusText))
            }
        }
        // 设置错误的监听函数
        xhr.onerror = function () {
            reject(new Error(this.statusText))
        }
        // 设置响应数据类型
        xhr.responseType = 'json'
    })
}

getAjax('http://xxxxxx').then(value => {
    console.log(value)
}).catch(reason => {
    console.log(reason)
})