【JavaScript】初识 Promise

52 阅读2分钟

出现原由

先看一个例子:

模拟发送表白信息,如果一个失败,那么再给其他人发送,这时就相当于在失败回调函数中套了一层回调;如果后续还有多个表白对象,那么将一层一层地嵌套下去,也就是回调地狱。

/**
 * 模拟发送表白信息
 * @param name 表白对象名字
 * @param onFulfilled 成功回调
 * @param onRejected 失败回调
 */
function sendMessage(name, onFulfilled, onRejected) {
    // 发送表白信息
    console.log(`小丑丑 -> ${name}: 我有一件重要的事想对你说,我喜欢你!`)
    console.log(`等待${name}回复...`)
    // 模拟女生回复
    setTimeout(()=> {
        // 有百分之十概率成功
        if (Math.random() <= 0.1) {
            onFulfilled(`${name} -> 小丑丑:我们在一起吧~`)
        } else {
            onRejected(`${name} -> 小丑丑:你是个好人,但是我们不合适。`)
        }
    }, 1000)
}
sendMessage('白月光', message => {
    console.log('成功:', message)
}, message => {
    console.log('失败:', message)
})

image.png

Promise 规范

Promise 是一套专门处理异步场景的规范,它能有效的避免回调地狱的产生,使异步代码更加清晰、简洁、统一。

Promise A+规定:

  1. 所有的异步场景,都可以看作是一个异步任务,每个异步任务,在 js 中应该表现为一个对象(比如上面的 sendMessage )该对象称为Promise对象,也叫做任务对象,例如远程登录、延时弹窗。

  2. 每个任务对象(Promise),都应该有两个阶段、三个状态。

    unsettled(pending) -> settled(fulfilled / rejected)

    • 挂起 -> 完成 :resolve(message)
    • 挂起 -> 失败 :reject(error)
  3. 对于任务的后续处理,完成状态的后续为 onFulfilled,失败的后续处理为 onRejected。

image.png

练习

延迟执行函数

/**
 * 延迟执行函数
 * @param duration 延迟时间(单位:毫秒)
 * @returns {Promise<unknown>}  返回任务对象 
 */
function delay(duration) {
    return new Promise((resolve) => {
        setTimeout(() => {
            resolve()
        }, duration)
    })
}
delay(1000).then(() => {
    console.log('1 second later')
})

元素创建

<!DOCTYPE html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<style>

</style>
<body>
<div class="container"></div>
<div class="label"></div>
<script>
    //根据指定的图片路径,创建一个img元素
    //该函数需要返回一个Promise,当图片加载完成后,任务完成,若图片加载失败,任务失败
    //任务完成后,需要提供的数据是图片D0M元素;任务失败时,需要提供失败的原因
    //提示:img元素有两个事件,1oad事件会在图像加载完成时触发,error事件会在图像加载失败时触发
    function createImg(imgUrl) {
        return new Promise((resolve, reject) => {
            let img = document.createElement('img')
            img.src = imgUrl
            img.onload = () => {
                resolve(img)
            }
            img.onerror = (event) => {
                reject(event)
            }
        })
    }

    // 有无防盗链
    const imgError = 'https://bkimg.cdn.bcebos.com/pic/86d6277f9e2f070828386172686eaf99a9014c085424?x-bce-process=image/format,f_auto/resize,m_lfit,limit_1,h_460'
    const imgSuccess = 'https://img.zcool.cn/community/011a5357b64c620000018c1b9e7e67.png@2o.png'
    // createImg(imgSuccess)
    //使用createImage函数创建一个图像,图像路径自行定义
    //当图像成功加载后,将图像宽高显示在元素中,当图像加载失败后,输出加载失败的原因
    createImg(imgError).then((img) => {
        const p = document.querySelector('.label')
        p.innerHTML = `图像的宽高分别为:${img.width} - ${img.height}`
    }, (error) => {
        console.log(error)
    })
    //使用createImage函数创建一个图像,图像路径自行定义
    //当图像成功加载后,将图像元素加入到container容器中,当图像加载失败后,输出加载失败的原因
    createImg(imgSuccess).then((img) => {
        const container = document.querySelector('.container')
        container.appendChild(img)
    }, (error) => {
        console.log(error)
    })
</script>
</body>
</html>