在回调的章节中,我们使用回调函数来接收执行的结果。很多时候,我们的需求都是固定的,我们需要获知执行状态,执行结果要不成功要不失败。因此 JavaScript 提供了 Promise 对象封装了这些相关的属性和方法。

语法如下:

let promise = new Promise(function(resolve, reject) {
    // resolve 执行成功调用
    // reject 执行失败调用
});

Promise 接收一个函数,函数有两个参数 resolve、reject:

  • resolve:应在任务执行成功时调用。
  • reject:应在任务执行失败时调用。

我们改写一下上节课回调的例子:

$$jsdemo$$
$$edit$$
function loadFile() {
    // 读取文件需要三秒钟
    return new Promise(function (resolve) {
        setTimeout(function () {
            const file = "三眼鸭的编程教室"
            // 将结果作为参数传到 resolve 中
            resolve(file)
        }, 3000)
    })
}
console.log("开始执行") // 同步执行

const promise = loadFile()

promise.then(function (file) {
    // 得到执行结果
    console.log(`文件内容:${file}`) // 文件内容:三眼鸭的编程教室
})

console.log("结束执行") // 同步执行

我们把回调函数作为 then 的第一个参数。而此时,通过执行 resolve 可以调用我们传进去的回调函数。

then、catch、finally

Promise 有三个方法,then、 catch、 finally,可供我们传递回调函数来处理执行结果。

then

then 可以接收两个函数作为参数,分别处理 resolvereject 的结果,语法如下:

promise.then(
    function (result) {
        /* 处理执行成功的结果 */
    },
    function (error) {
        /* 处理执行失败的结果 */
    }
)
$$jsdemo$$
$$edit$$
function loadFile() {
    // 读取文件需要三秒钟
    return new Promise(function (resolve, reject) {
        setTimeout(function () {
            // 假设有一半的概率读取失败
            if (Math.random() < 0.5) {
                const file = "三眼鸭的编程教室"

                // 将结果作为参数传到 resolve 中
                resolve(file)
            } else {
                // 失败
                reject("网络中断")
            }
        }, 3000)
    })
}
console.log("开始执行") // 同步执行

const promise = loadFile()

promise.then(
    function (file) {
        // 得到执行结果
        console.log(`文件内容:${file}`) // 文件内容:三眼鸭的编程教室
    },
    function (error) {
        // 失败处理
        console.log(`失败,原因是:${error}`)
    }
)

console.log("结束执行") // 同步执行

catch

catch 接收一个函数作为参数,用以处理执行失败的结果,语法如下:

promise.catch(function (error) {
    /* 处理执行失败的结果 */
})

当然,这跟我们设置 then 的第一个参数为 null,只设置第二个参数等价的:

promise.then(
    null, 
    function (error) {
    /* 处理执行失败的结果 */
})

finally

不管执行成功还是执行失败都会被执行。

promise.finally(
    function () {
    /* 总是会执行 */
})

Promise 链

promise 的回调中 return 的值会作为下一个 then 中的参数,这便被称为 Promise 链。

$$jsdemo$$
$$edit$$
let promise = new Promise(function (resolve, reject) {
    resolve("三眼鸭")
})

promise
    .then(function (value) {
        console.log(value) // 三眼鸭
        return "三眼鸡"
    })
    .then(function (value) {
        console.log(value) // 三眼鸡
        return "三眼鹅"
    })
    .then(function (value) {
        console.log(value) // 三眼鹅
    })

如果返回的是一个 promise, 那么会等待其结果后。

$$jsdemo$$
$$edit$$
let promise = new Promise(function (resolve, reject) {
    resolve("三眼鸭")
})

promise
    .then(function (value) {
        console.log(value) // 三眼鸭
        return new Promise(function (resolve, reject) {
            setTimeout(() => resolve("三眼鸡"), 3000)
        })
    })
    .then(function (value) {
        console.log(value) // 三眼鸡
    })

如果抛出了一个错误,那么便会被接下来的 catch 捕获到。

$$jsdemo$$
$$edit$$
let promise = new Promise(function (resolve, reject) {
    resolve("三眼鸭")
})

promise
    .then(function (value) {
        throw "错误"
    })
    .catch(function (error) {
        console.log(error) // 错误
    })

Promise.all

Promise.all 接收 promise 的可迭代对象(Array、Set 等),并返回一个新的 promisePromise.allpromise 在可迭代对象的所有 promise 执行完成后执行,并且 resolve 的参数为所有可迭代对象的 promise 结果。

$$jsdemo$$
$$edit$$
function after1s() {
    return new Promise(function (resolve) {
        setTimeout(function () {
            resolve("1s")
        }, 1000)
    })
}

function after3s() {
    return new Promise(function (resolve) {
        setTimeout(function () {
            resolve("3s")
        }, 3000)
    })
}

Promise.all([after1s(), after3s()]).then(function (result) {
    console.log(result) // ['1s', '3s']
})

如果可迭代对象中任意一个 promise 执行 reject 则立刻执行Promise.allpromisereject

$$jsdemo$$
$$edit$$
function after1s() {
    return new Promise(function (resolve, reject) {
        setTimeout(function () {
            reject(new Error("执行出错"))
        }, 1000)
    })
}

function after3s() {
    return new Promise(function (resolve) {
        setTimeout(function () {
            resolve("3s")
        }, 3000)
    })
}

Promise.all([after1s(), after3s()])
    .then(function (result) {
        console.log(result)
    })
    .catch(function (error) {
        console.log(error.message) // 执行出错
    })

练习

  1. 使用 promise 改写以下的回调函数。
$$jsdemo$$
$$edit$$
function doAfter3s(callback) {
    setTimeout(function () {
        callback("三眼鸭的编程教室")
    }, 3000)
}

doAfter3s((word) => alert(word))

$$answer

$$jsdemo$$
$$edit$$
function doAfter3s() {
    return new Promise(function (resolve) {
        setTimeout(function () {
            resolve("三眼鸭的编程教室")
        }, 3000)
    })
}

doAfter3s().then((word) => alert(word))

$$

  1. 有以下几个 promise 函数,求出所有 resolve 结果的和。
function x() {
    return new Promise(function (resolve) {
        setTimeout(function () {
            resolve(10)
        }, 3000)
    })
}

function y() {
    return new Promise(function (resolve) {
        setTimeout(function () {
            resolve(5)
        }, 1000)
    })
}

function z() {
    return new Promise(function (resolve) {
        setTimeout(function () {
            resolve(20)
        }, 2000)
    })
}

$$answer

$$jsdemo$$
$$edit$$
function x() {
    return new Promise(function (resolve) {
        setTimeout(function () {
            resolve(10)
        }, 3000)
    })
}

function y() {
    return new Promise(function (resolve) {
        setTimeout(function () {
            resolve(5)
        }, 1000)
    })
}

function z() {
    return new Promise(function (resolve) {
        setTimeout(function () {
            resolve(20)
        }, 2000)
    })
}

Promise.all([x(), y(), z()]).then(function (result) {
    alert(result.reduce((a, b) => a + b))
})

$$