Promise 学习笔记

一、Promise 简介

​ Promise 是 JS 中进行异步编程的新解决方案。(旧方案是单纯使用回调函数)

​ 优点:

  • 指定回调函数更加灵活
  • 支持链式调用,可以解决毁回调地狱问题

二、Promise 初体验

​ resolve、reject 两者都是函数类型的数据,分别为成功和失败后的回调。

​ 在内部封装时可以携带参数。

const p = new Promise((resolve, reject) => {
    setTimeout(() => {
        if (xxx) {
            resolve(arg1);	//将 promise 对象的状态设置为成功
        }
        else {
            reject(arg2);	//将 promise 对象的状态设置为失败
        }
    }, 1000)
})

// 第一个为成功回调,第二个为失败回调
p.then((arg) => {
    alert('success, the arg is ' + arg);
}, (arg) => {
    alert('failed' + arg);
})

​ 实际使用中也可以这样封装:

function asyncTask(arg) {
    return new Promise((resolve, reject) => {
        if (xxx) {
            resolve();
        }
        else {
            reject();
        }
    })
}

const obj = asyncTask(xxx);
obj.then((arg1) => {
    ...
}, (arg2) => {
    ...
})

util.promisify 方法:

// 可以借助 node.js 的该方法来将函数转化为 promise 风格
const util = require('util');
const fs = require('fs');

let mineReadFile = util.promisify(fs.readFile);
mineReadFile(...).then(...);

三、Promise 状态与值

​ 指实例对象中的一个属性:PromiseState

​ 只有以下两种转变情况:

pending -> fulfilled

pending ->rejected

​ 且一个 promise 对象只能改变一次状态。无论变为成功还是失败,都会有一个结果数据。成功的结果数据一般称为 value,失败的结果数据一般称为 reason。

​ Promise 对象的值:

​ 实例对象中的另一个属性 PromiseResult,保存着对象成功/失败的结果


四、Promise 的基本流程

image-20220418213510140


五、Promise API

1、构造函数

executor = (resolve, reject) => {};
Promise(excutor) {};

​ 注:executor 函数会在 Promise 内部立即同步调用,异步操作在执行器中执行。

2、Promise.prototype.then()

Promise.prototype.then(onResolved,onRejected) => {};
  • onResolved 函数:成功的回调函数
  • onRejected 函数:失败的回调函数

​ 注:指定用于得到成功 value 的成功回调和用于得到失败 reason 的失败回调,返回一个新的 promise 对象

3、Promise.prototype.catch()

Promise.prototype.catch(reason => {});

​ 只用于指定失败的回调。

4、Promise.resolve()

var obj = Promise.resolve(arg);

​ 如果传入的是非 Promise 对象,则返回结果为成功 Promise 对象。

​ 如果传入的是 Promise 对象,参数的结果决定了 resolve 的结果。

​ 如下方返回一个成功 Promise 对象:

let p1 = Promise.resolve(new Promise((resolve, reject) => {
    // resolve('It\' OK.');
    reject('Error');
}))
console.log(p1);
// catch
p1.catch(reason => {
    console.log(reason);
})

5、Promise.reject()

var obj = Promise.reject(arg);

​ 传入任何类型值,返回都为失败 Promise 对象,而失败的结果是传递的值或对象

​ 如下方返回一个失败 Promise 对象

let p2 = Promise.resolve(new Promise((resolve, reject) => {
    resolve('It\' OK.');
}))
console.log(p2);

6、Promise.all()

Promise.all((promises) => {})

​ 说明:返回一个新的 promise,只有所有的 promise 都成功才成功,只要有一个失败了就直接失败。

​ 成功返回结果是所有 promise 对象返回结果的数组,

​ 失败返回结果是失败 promise 对象的返回结果。

7、Promise.race()

Promise.race((promises) => {})

​ 说明:返回一个新的 promise,第一个完成(改变状态)的 promise 的结果状态就是最终的结果状态。


六、Promise 关键问题

1、Promise 状态改变的方法

  • resolve(value):如果当前是 pending 就会变为 resolved
  • reject(reason):如果当前是 pending 就会变为 rejected
  • 抛出异常:如果当前是 pending 就会变为 rejected
let p = new Promsie((resolve, reject) => {
    // resolve
    resolve('OK');
    // reject
    reject('error');
    // throw exception
    throw "Exception!";
})

2、指定多个成功/失败回调

​ 一个 Promise 指定多个成功/失败回调,当promise改变为对应状态时都会调用

let p = new Promise((resolve, reject) => {
    resolve('OK');
})
p.then(value => {
    console.log(value+'!');
})
p.then(value => {
    console.log(value+'!!');
})

3、改变状态与指定回调谁先谁后

​ 都有可能。但一般执行器内是异步任务,因此一般是先指定回调。

​ 若要先改变状态:

  • 执行器内直接改变状态
  • 延迟更长时间再调用 then()

​ 得到数据的时间:

  • 如果先指定的回调,那当状态发生改变时,回调函数就会调用,得到数据。
  • 如果先改变的状态,那当指定回调时,回调函数就会调用,得到数据。

4、then() 方法返回结果

​ 主要有三种情况:

let res = p.then(value => {
    // 1. throw exception,那么失败,结果是异常
    throw 'An exception!';
    // 2. 返回结果是非 Promise 对象,那么成功,结果是该值
    return 521;
    // 3. 返回结果是 Promise 对象,那么成功和返回值都由该对象决定
    return new Promise((resolve, reject) => {
        // resolve('OK');
        reject('error');
    })
})

5、Promise 串联任务

​ 比如串联两个异步任务与一个同步任务:

let p = new Promise((resolve, reject) => {
    // 第一个异步 Promise
    setTimeout(() => {
        resolve('First OK');
    }, 1000);
}).then(value => {
    // 成功后告知第一个异步 Promise 完成,输出 'First OK'
    console.log(value);
    // 回调第二个异步 Promise
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve('Second OK');
        }, 2000);
    })
}).then(value => {
    // 成功后告知第二个异步 Promise 完成,输出 'Second OK'
    console.log(value);
    // 该回调返回成功 Promise
}).then(value => {
    // 第三个 Promise 回调开始,输出 undefined
    console.log(value);
})

6、Promise 异常穿透

​ Promise 链中任意层的失败可以穿透至最后层,而被捕获的现象。

​ 如:

// 中间任意层可以不指定失败捕获
new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('OK');
    }, 1000);
}).then(value => {
    throw 'Second layer exception!';
}).then(value => {
    console.log(3);
}).then(value => {
    console.log(4);
}).catch(reason => {
    console.err(reason);
})

7、中断 Promise 链

​ 有且只有一个方法:返回一个 pending 状态的 Promise 对象。

...
.then(value => {
    ...
    return new Promise(() => {});
})
...

七、async 与 await

1、async 函数

​ async 函数的返回值为 Promise 对象,且其结果同样由函数执行的返回值决定。


2、await 表达式

  • await 右侧的表达式一般为 promise 对象,但也可以是其它的值
  • 如果表达式是 promise 对象,await 返回的是 promise 成功的值
  • 如果表达式是其它值,直接将此值作为 await 的返回值

​ 注:

  • await 必须写在 async 函数中,但 async 函数中可以没有 await
  • 如果 await 的 Promise 失败了,就会抛出异常,需要捕获

3、结合使用的例子

​ 异步依次读取文件:

const fs = require('fs');
const util = require('util');
const mineReadFile = util.promisify(fs.readFile)

!async function main() {
    try {
        let data1 = await mineReadFile('./1.txt');
        let data2 = await mineReadFile('./2.txt');
        let data3 = await mineReadFile('./3.txt');
        console.log(data1 + data2 + data3);
    }
    catch(e) {
        console.warn(e);
    }
}();

Q.E.D.


f"{言语过于单薄,于此引用心声}"