ES6--Promise

89 阅读4分钟

Promise

Promise 的定义和使用

Promise 是 ES6 引入的异步编程的新解决方案。语法上 Promise 是一个构造函数,用来封装异步操作并可以获取其成功或失败的结果。

一个 Promise 必然处于以下几种状态之一:

  • 待定(pending):初始状态,既没有被兑现,也没有被拒绝。
  • 已兑现(fulfilled):意味着操作成功完成。
  • 已拒绝(rejected):意味着操作失败。

Promise 的使用:

  • Promise 构造函数:new Promise((resolve, reject)=>{})
  • Promise.prototype.then 方法
  • Promise.prototype.catch 方法

一个简单案例:

let p = new Promise(function (resolve, reject) {
    // 使用 setTimeout 模拟请求数据库数据操作
    setTimeout(function () {
        // 这个异步请求数据库数据操作是否正确返回数据
        let isRight = true;
        if (isRight) {
            let data = '数据库中的数据';
            // 设置 Promise 对象的状态为操作成功
            resolve(data);
        } else {
            let err = '数据读取失败!'
            // 设置 Promise 对象的状态为操作失败
            reject(err);
        }
    }, 1000);
});
p.then(function (value) {
    console.log(value);
}, function (reason) {
    console.error(reason);
})Copy to clipboardErrorCopied

Promise 封装读取文件

// 使用 nodejs 的 fs 读取文件模块
const fs = require('fs');

const p = new Promise(function (resolve, reject) {
    fs.readFile('./resources/为学.txt', (err, data) => {
        // err 是一个异常对象
        if (err) reject(err);
        resolve(data);
    })
})

p.then(function (value) {
    // 转为字符串输出
    console.log(value.toString());
}, function (reason) {
    console.log('读取失败!!');
})Copy to clipboardErrorCopied

Promise 封装 Ajax 请求

const p = new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();
    xhr.open('get', 'https://api.apiopen.top/getJoke');
    xhr.send();
    xhr.onreadystatechange = function () {
        if (xhr.readyState === 4) {
            if (xhr.status >= 200 && xhr.status < 300) {
                // 成功
                resolve(xhr.response);
            } else {
                // 失败
                reject(xhr.status);
            }
        }
    }
});

// 指定回调
p.then(function (value) {
    console.log(value);
}, function (reason) {
    console.error(reason);
})Copy to clipboardErrorCopied

Promise.prototype.then 方法

先复习一下一个 Promise 的三种状态:

  • 待定(pending):初始状态,既没有被兑现,也没有被拒绝。
  • 已兑现(fulfilled):意味着操作成功完成。
  • 已拒绝(rejected):意味着操作失败。

Promise.prototype.then 方法返回的结果依然是 Promise 对象,对象状态由回调函数的执行结果决定

具体情况如下:

  • 若 then 方法没有返回值,则 then 方法返回的对象的状态值为成功 fulfilled,返回结果值为 undefined

    const p = new Promise((resolve, reject) => {
        setTimeout(() => {
            // resolve('用户数据')
            reject('出错了');
        }, 1000);
    })
    // 未设定返回值
    const res = p.then((value) => {
        console.log(value);
    }, (reason) => {
        console.warn(reason);
    })
    // 打印 then 方法的返回值
    console.log(res);Copy to clipboardErrorCopied
    
  • 如果回调函数中返回的结果是非 Promise 类型的属性,则 then 方法返回的对象,其状态为成功(fulfilled),返回结果值取决于 then 方法所执行的是哪个函数(resolve 或 reject)。

    const p = new Promise((resolve, reject) => {
        setTimeout(() => {
            // resolve('用户数据')
            reject('出错了');
        }, 1000);
    })
     // 返回的非 Promise 对象
    const res = p.then((value) => {
        console.log(value);
        return '成功了!!';
    }, (reason) => {
        console.warn(reason);
        return '出错啦!!'
    })
    // 打印 then 方法的返回值
    console.log(res);Copy to clipboardErrorCopied
    
  • 如果回调函数中返回的结果是 Promise 类型(return new Promise()),则 then 方法返回的 Promise 对象状态与该返回结果的状态相同,返回值也相同。

    const p = new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve('用户数据')
            // reject('出错了');
        }, 1000);
    })
    const res = p.then((value) => {
        console.log(value);
        // 返回 Promise 对象
        return new Promise((resolve, reject) => {
            resolve('(1)成功了!!!');
            // reject('(1)出错了!!!')
        })
    }, (reason) => {
        console.warn(reason);
        return new Promise((resolve, reject) => {
            // resolve('(2)成功了!!!');
            reject('(2)出错了!!!')
        })
    })
    // 打印 then 方法的返回值
    console.log(res);Copy to clipboardErrorCopied
    
  • 如果回调函数中返回的结果是 throw 语句抛出异常,则 then 方法的对象的状态值为 rejected,返回结果值为 throw 抛出的字面量或者 Error 对象。

    const p = new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve('用户数据');
        }, 1000);
    });
    const res = p.then((value) => {
        console.log(value);
        return new Promise((resolve, reject) => {
            throw new Error('错误了!!');
        })
    });
    // 打印结果
    console.log(res);Copy to clipboardErrorCopied
    

链式调用

Promise.prototype.then 方法返回的结果还是 Promise 对象,这意味着我们可以继续在该结果上使用 then 方法,也就是链式调用。

const p = new Promise(resolve=>{}, reject=>{});
p.then(value=>{}, reason=>{})
.then(value=>{}, reason=>{})
.then(value=>{}, reason=>{})
...Copy to clipboardErrorCopied

链式调用练习-多个文件读取

const fs = require('fs');

let p = new Promise((resolve, reject) => {
    fs.readFile('./resources/users.md', (err, data) => {
        // 传给下一轮文件读取操作
        resolve(data);
    })
});

p.then(value => {
    return new Promise((resolve, reject) => {
        // value 为第一次读取的文件数据,data 为第二次(当前)读取的数据
        fs.readFile('./resources/orders.md', (err, data) => {
            // 将上轮读取结果和本轮合并传到下一轮轮读取操作
            resolve([value, data]);
        });
    });
}).then(value => {
    return new Promise((resolve, reject) => {
        fs.readFile('./resources/goods.md', (err, data) => {
            // value 为上一轮传递过来的文件数据数组
            value.push(data);
            // 传给下一轮操作
            resolve(value);
        });
    });
}).then(value => {
    // 合并数组元素,输出
    console.log(value.join('\n'));
});Copy to clipboardErrorCopied

Promise.prototype.catch

catch() 方法返回一个 Promise,并且处理拒绝的情况。它的行为与调用 Promise.prototype.then(undefined, onRejected) 相同。

即:

obj.catch(onRejected);Copy to clipboardErrorCopied

等同于:

obj.then(undefined, onRejected);Copy to clipboardErrorCopied

语法:

p.catch(onRejected);

p.catch(function(reason) {
   // 拒绝
});Copy to clipboardErrorCopied

举例:

var p1 = new Promise(function (resolve, reject) {
    resolve('Success');
});

p1.then(function (value) {
    console.log(value); // "Success!"
    throw 'oh, no!';
}).catch(function (e) {
    console.log(e); // "oh, no!"
}).then(function () {
    console.log('有 catch 捕获异常,所以这句输出');
}, function () {
    console.log('没有 catch 捕获异常,这句将不会输出');
});Copy to clipboardErrorCopied

输出结果:

Success
oh, no!
有 catch 捕获异常,所以这句输出Copy to clipboardErrorCopied

以上只是 Promise 的入门,更多还要进一步深入学习。