promise 规范应用

promise 规范及应用

Promises / PromiseA+ 规范

PromiseA+ 规范

  • promise:一个标准、规范
  • 三种状态:pendingfulfilledreject,状态一旦改变不允许再修改
js 复制代码
let promise_0 = new Promise((resolve) => {
    setTimeout(() => {
        resolve("promise_0");
    }, 100);
});

let promise_1 = Promise.resolve(promise_0);
// promise_1 会在  promise_0 fulfilled时,自动以同样的值 fulfilled

let thenable = {
    then: function (resolve, reject) {
        setTimeout(() => resolve("success"), 100);
    },
};
let promise_2 = Promise.resolve(thenable);
// promise_2 最终 fulfilled,值为 "success"
  1. 状态

    • Promise 必须从 pending 转为 fulfilled 或 rejected,且一旦转换之后,状态和值(或原因)就固定下来,不会再变
    • 例如:一个异步操作,在成功时调用 resolve 并传入数据,在出错时候调用 reject 并传入错误原因
  2. then 方法

    • then 接受两个回调,分别用于处理成功和失败

    • 如果回调返回值,则将值传递给下一个 then,如果抛出异常,则下一个 then 会接收到拒绝原因

      js 复制代码
      someAsyncOperation().then((value) => {
       console.info("success", value);
       return value + 1;
      }).then((value) => {
       console.info("链式调用", value);
      }).catch((reason) => {
       console.error("error", reason
      })
  3. Promise 解析过程

    • 如果一个 then 回调返回了另一个 Promise,那么当前 Promise 就会 "跟随"这个返回的 Promise 状态,并且采用它的值

传统异步任务的处理

  • 回调函数

    • 一个简单的请求:

      • 调用一个函数,这个函数中返回网络请求
      • 发送网络请求成功,就告知调用者成功,并返回数据
      • 发送网络请求失败,就告知调用者失败,并返回失败信息
    js 复制代码
    function request(cb) {
        // 模拟发送网络请求
        let flag = Math.random() > 0.5 ? true : false;
        setTimeout(() => {
          cb(flag ? "success" : "fail"
        },500)
    }
    // ...
    console.info("request: 发起请求")
    request((status,msg)=>{
        console.info("request: ",status,msg)
    })
    // 回调函数
    request((s1,m1)=>{
        // 业务处理逻辑1
        request((s2,m2)=>{
          // 业务处理逻辑2
            request((s3,m3)=>{
            // 业务处理逻辑3
                console.info("request: ",s3,m3)
        })
    })
  • 回调地狱

    • 如果是自己封装的请求方法,必须设计好规范,如果使用他人库,则必须要通过查看文档或源码才知道如何使用这个函数
    • 及其容易出现回调地狱

Promise

  1. 什么是 Promise
  • 是一个类,用来封装异步操作并获取其成功或失败的结果

  • 当通过 new 创建 Promise 实例,需要传入一个回调函数,我们称之为 executor

    • 这个回调函数会被立即执行,并传入两个参数 resolve 和 reject
    • 当调用 resolve 回调函数时,会执行 Promise 对象的 then 方法传入的回调
    • 当调用 reject 回调函数时,会执行 Promise 对象的 catch 方法传入的回调
  • Promise 是一个状态机,分为 pending、fulfilled、rejected 三个状态

    • pending:初始状态,执行了 executor 后,处于该状态
    • fulfilled:成功状态,调用 resolve() 后,Promise 的状态变为 fulfilled,且无法再改变
    • rejected:失败状态,调用 reject() 后,Promise 的状态变为 rejected,且无法再改变
js 复制代码
function request(cb) {
    // 模拟发送网络请求
    let flag = Math.random() > 0.5 ? true : false;
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            if (flag) {
                resolve("success");
                return;
            }
            reject("error");
        });
    }, 500);
}
console.info("request: 发起请求");
request().then(
    (msg) => console.info("request: ", msg),
    (err) => console.error("request: ", err)
);
  1. resolve 的参数
  • 如果传入的是普通的值或者对象,则会传递到 then 的参数中
  • 如果传入的是一个 Promise,那么当前的 Promise 状态会由传入的 Promise 决定
js 复制代码
const newPromise = new Promise((resolve, reject) => {
    resolve("success");
});

new Promise((resolve, reject) => {
    // 当前 Promise 的状态由 newPromise 决定
    resolve(newPromise);
})
    .then((res) => console.info(res)) // success
    .catch((err) => console.error(err));
  • 如果传入的是一个对象,并且该对象实现了 then 方法 (thenable),也会执行该 then 方法,并由该 then 方法决定后续的状态
js 复制代码
new Promise((resolve, reject) => {
    resolve({
        then(resolve, reject) {
            reject("error");
        },
    });
}).then(
    (res) => console.info(res),
    (err) => console.error(err) // error
);
  1. Promise 的实例方法
  • then 方法:通过 then 方法可以对 Promise 的 resolve 进行处理,then 方法的返回值是一个 Promise 实例

    • 多次调用 then 方法:同一个 Promise 实例可以多次调用 then 方法,当 Promise 中 resolve 被回调时,所有 then 方法传入的传入的函数都会被调用
js 复制代码
const promise_0 = new Promise((resolve, reject) => {
    resolve("success");
});
// 同时调用
promise_0.then((res) => console.info(res));
promise_0.then((res) => console.info(res));
promise_0.then((res) => console.info(res));
  • then 方法传入的回调函数可以有返回值

    • 如果返回的是普通值,那么这个普通值作为一个新的 Promise 的 resolve 的值
js 复制代码
const promise_1 = new Promise((resolve, reject) => {
    resolve("success");
});
promise_1.then(() => "then").then((res) => console.info(res)); // then

// promise_1.then(() => "then") 等价于
promise_1.then(() => {
    return new Promise((resolve) => {
        resolve("then");
    });
});
  • 如果返回的是 Promise,那么就可以再次调用 then 方法
  • 如果返回的是一个对象,并且该对象实现了 thenable,该 then 函数有两个参数 resolve,reject,则 resolve 会传递给下一个 Promise
js 复制代码
const promise_2 = new Promise((resolve, reject) => {
    resolve("success");
});
promise_2
    .then(() => {
        return {
            then(resolve) {
                return resolve("then"); // resolve 传递给下一个 Promise
            },
        };
    })
    .then((res) => console.info(res)); // then
  • catch 方法

    • 除了 then 方法的第二个参数来捕获 reject 之外, catch 方法用于处理 Promise 的 reject,catch 方法返回的也是一个 Promise 实例
js 复制代码
const promise_3 = new Promise((resolve, reject) => {
    resolve("success");
});
promise_3.then((undefined, err) => {
    // 打印 err
    console.error(err);
});
// 这种写法不太符合 `promise/A+ 规范`
promise_3.catch((err) => {
    // 打印 err
    console.error(err);
});
// 符合规范的写法
promise_3.then(
    () => {},
    (err) => {
        console.error(err);
    }
);
  • catch 方法也是可以多次调用的,只要 Promise 实例的状态是 reject,那么就会调用 catch 方法

  • finally 方法

    • 无论一个 Promise 实例是 fulfilled 还是 rejected,finally 方法都会被调用。finally 方法不接收参数,返回的也是一个 Promise 实例
js 复制代码
const promise_4 = new Promise((resolve, reject) => {
    resolve("success");
});
promise_4
    .then(() => {})
    .catch((err) => console.error(err))
    .finally(() => console.info("finally code execute"));
  • Promise 的类方法
  1. resolve 方法
js 复制代码
const foo = { name: "Fa-ce" };
function bar(obj) {
    return new Promise((resolve) => {
        resolve(obj);
    });
}
bar(foo).then((res) => console.info(res)); // { name: 'Fa-ce' }
  • resolve 参数:

    • 参数本身是 Promise
    • 参数是原始值/对象
    • 参数是一个 thenable
  1. reject 方法
  2. all 方法
  3. race 方法
  4. allSettled 方法
  5. any 方法
  • all 方法:接收一个 Promise[],返回一个 Promise 实例,当所有的 Promise 执行完毕且都是 fulfilled 状态时,该实例的状态才会变成 fulfilled,只要队列中有一个实例的状态是 rejected,那么该实例的状态就会变成 rejected

    • 如果 Promise 队列中所有的实例状态都是 fulfilled,那么 Promise.all() 方法返回的实例状态就会变成 fulfilled,并且 then() 的参数使用给数组,按照顺序放置队列中每个 Promise 成功后的结果
js 复制代码
let i = 0;
function genPromise() {
    return new Promise((resolve) => {
        resolve(`success${i++}`);
    });
}

const promiseArr = [genPromise(), genPromise(), genPromise()];
Promise.all(promiseArr).then((res) => console.info(res)); // ['success0', 'success1', 'success2']
  • 如果队列中 Promise 实例有一个是 rejected,那么 Promise.all() 方法返回的实例状态就会变成 rejected,并且 reject() 的参数使用给第一个 rejected 的结果
js 复制代码
let i = 0;
function genPromise() {
    return new Promise((resolve) => {
        resolve(`success${i++}`);
    });
}
const promiseArr = [
    genPromise(),
    new Promise((resolve, reject) => reject("error1")),
    new Promise((resolve, reject) => reject("error2")),
    genPromise(),
];
Promise.all(promiseArr).catch((err) => console.info(err)); // error1
  • allSettled 方法:该方法返回的 Promise 实例,会在所有 Promise 实例执行完毕后,状态变成 fulfilled,并且只会是 fulfilled

    • 无论队列中的 Promise 实例的状态如何,都能取到结果
    • 打印的结果,会包含状态以及值/原因
js 复制代码
let i = 0;
function genPromise() {
    return new Promise((resolve) => {
        resolve(`success${i++}`);
    });
}
const promiseArr = [
    genPromise(),
    new Promise((resolve, reject) => reject("error1")),
    new Promise((resolve, reject) => reject("error2")),
    genPromise(),
];
Promise.allSettled(promiseArr)
    .then((res) => console.info(res))
    .catch((err) => console.info(err));
  • race 方法

    • Promise.race() 方法接收一个Promise数组,返回一个Promise实例,只要数组中的某个Promise实例状态改变,那么返回的Promise实例状态就会改变,并且使用第一个改变状态的Promise实例的值/原因
    • 返回最先执行完的Promise实例
js 复制代码
const promiseArr = [
    new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve("success");
        }, 1000);
    }),
    new Promise((resolve, reject) => {
        setTimeout(() => {
            reject("error");
        }, 1500);
    }),
    new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve("success");
        }, 600);
    }),
];
Promise.race(promiseArr)
    .then((res) => console.info(res)) // success
    .catch((err) => console.info(err));
  • any 方法

    • Promise.any() 方法会等待一个fulfilled状态,才会决定返回的Promise实例的状态
    • 如果队列中所有的实例都是reject,那么也需要等所有执行完毕后才返回,并且返回的实例状态是rejected,并且使用第一个reject的值/原因
js 复制代码
const promiseArr = [
    new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve("success");
            // reject("reject-0");
        }, 1000);
    }),
    new Promise((resolve, reject) => {
        setTimeout(() => {
            reject("error");
        }, 1500);
    }),
    new Promise((resolve, reject) => {
        setTimeout(() => {
            // resolve("success");
            reject("reject_2");
        }, 600);
    }),
];

Promise.any(promiseArr)
    .then((res) => console.info(res))
    .catch((err) => console.info(err));
// 遇到第一个 fulfilled时,转变返回的 Promise 实例状态
// 如果所有都是 rejected,那么只有所有执行完毕后,才会返回

Promise 回调和 async/await

js 复制代码
Promise.resolve("green").then((result) => {
    console.log(result);

    Promise.resolve("red").then((result) => {
        console.log(result);

        Promise.resolve("yellow").then((result) => {
            console.log(result);
        });
    });
});
js 复制代码
async () => {
    await Promise.resolve("green").then((res) => {
        console.log(res);
    });
    await Promise.resolve("red").then((res) => {
        console.log(res);
    });
    await Promise.resolve("yellow").then((res) => {
        console.log(res);
    });
};
// 浏览器不直接支持 async/await,需要 babel 转译
  • async 是 function 的一个前缀,只有 async 函数中才能使用 await 语法
  • async 函数是一个 Promise 对象,有无 resolve 取决于有无函数中 return 值
  • await 后边跟得是一个 Promise 对象,如果不是,则会包裹一层 Promise.resolve(),将其转化为 Promise 对象

await 只能在 async 函数中使用,不能在普通函数中使用,它会暂停函数执行, 直到 等待的 Promise 成功或者失败

js 复制代码
async function fetchData() {
    return "Data fetched successfully";
}

async function showData() {
    const data = await fetchData();
    console.log(data);
}

/* ************ */
async function processData() {
    try {
        const data = await fetchData();
        console.log(data);
    } catch (err) {
        console.log(err);
    }
}
js 复制代码
async function processSequentAll() {
    const p1 = await fetchData1();
    const p2 = await fetchData2(data2);
    const p3 = await fetchData3(data3);
}
// 或者
async function processSequentAll() {
    const p1 = fetchData1();
    const p2 = fetchData2(data2);
    const p3 = fetchData3(data3);

    const result = await Promise.all([p1, p2, p3]);
    console.log(result);
}
  • 从 async/await 到 promise
js 复制代码
function fetchDataAsPromise() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve("data fetched successfully");
        }, 1000);
    });
}
// 从 promise 到 async
async function useFetchDataAsPromise() {
    const data = await fetchDataAsPromise();
    console.log(data);
}
useFetchDataAsPromise();
相关推荐
墨绿色的摆渡人7 分钟前
论文笔记(七十五)Auto-Encoding Variational Bayes
前端·论文阅读·chrome
今晚吃什么呢?28 分钟前
前端面试题之CSS中的box属性
前端·css
我是大龄程序员31 分钟前
Babel工作理解
前端
CopyLower44 分钟前
提升 Web 性能:使用响应式图片优化体验
前端
南通DXZ1 小时前
Win7下安装高版本node.js 16.3.0 以及webpack插件的构建
前端·webpack·node.js
Mintopia1 小时前
深入理解 Three.js 中的 Mesh:构建 3D 世界的基石
前端·javascript·three.js
前端太佬2 小时前
暂时性死区(Temporal Dead Zone, TDZ)
前端·javascript·node.js
Mintopia2 小时前
Node.js 中 http.createServer API 详解
前端·javascript·node.js
xRainco2 小时前
Redux从简单到进阶(Redux、React-redux、Redux-toolkit)
前端
印第安老斑鸠啊2 小时前
由一次CI流水线失败引发的对各类构建工具的思考
前端