简介

说明

本文用示例介绍JavaScript的Promise。它是ES6新增的特性。

Promise的用途


  1. 用来异步编程
  2. 解决回调地狱(也称为:厄运金字塔)
  1. 在异步调用中再次进行异步回调,如果嵌套的层级太多,会导致难以维护。

Promise 的三种状态


  1. pending:等待(初始状态)

  1. 在 resolve 被调用时变为 "fulfilled"。
  2. 在reject 被调用时变为 "rejected" 。

  1. fulfilled:已完成
  2. rejected:已失败。(已拒绝)

状态变为resolved 或 rejected 之后,就不会再次改变。这两个状态被称为 “settled”(已定型)。

用法简介

简述

Promise 都有这些内容:构造函数、对象的实例方法、静态方法


  1. 1个构造函数:new Promise
  2. 3个对象的实例方法:.then 和 .catch 和 finally(不常用)
  3. 5个静态方法:Promise.all、Promise.allSettled、Promise.race、Promise.resolve、Promise.reject

then与catch

        .catch(f) 调用是 .then(null, f) 的完全的模拟,它只是一个简写形式。

        then与catch只用一个即可。一般用then,因为它可以处理成功(fulfilled)与失败(rejected),而catch只能处理失败(rejected)。

下边程序中,会进行reject,后续then会执行,.catch不会执行,finally会执行。

let promise = new Promise(function (resolve, reject) {
setTimeout(() => {
reject("定时任务结果(reject)");
}, 1000);
});

promise
.then(
result => console.log("成功(then):" + result),
error => console.log("错误(then):" + error)
)
.catch(
error => console.log("错误(catch):" + error)
)
.finally(
() => console.log("运行结束(finally)")
);

结果

错误(then):定时任务结果(reject)
运行结束(finally)

resolve与reject

resolve方法:

        通过回调里的 resolve(data) 将这个状态标记为 fulfilled,然后进行下一步 then((data)=>{//do something})。resolve 里的data就是你要传入 then 的data。

reject方法:

        通过回调里的 reject(data) 将这个状态标记为 rejected,然后进行下一步 then((data1, data2)=>{//do something})。resolve 里的data就是你要传入 then 的data2。

executor 只能调用一个 resolve 或一个 reject ,所有其他的 resolve 和 reject 的调用都会被忽略:

let promise = new Promise(function(resolve, reject) {
resolve("done");
reject(new Error("…")); // 被忽略
setTimeout(() => resolve("…")); // 被忽略
});

示例:异步编程

示例1:resolve(成功)

let promise = new Promise(function (resolve, reject) {
setTimeout(() => {
resolve("定时任务结果(resolve)");
}, 1000);
});

promise
.then(
result => console.log("成功(then):" + result),
error => console.log("错误(then):" + error)
)
.finally(
() => console.log("运行结束(finally)")
);

结果

成功(then):定时任务结果(resolve)
运行结束(finally)

示例2:reject(失败)

let promise = new Promise(function (resolve, reject) {
setTimeout(() => {
reject("定时任务结果(reject)");
}, 1000);
});

promise
.then(
result => console.log("成功(then):" + result),
error => console.log("错误(then):" + error)
)
.finally(
() => console.log("运行结束(finally)")
);

结果

错误(then):定时任务结果(reject)
运行结束(finally)

示例:解决回调地狱

什么是回调地域?

在异步调用中再次进行异步回调,如果嵌套的层级太多,会导致难以维护。这就是回调地狱。

例如:使用ajax请求url,成功后再请求url1,成功后再请求url2,代码会是下边这个样子:

ajax(url, () => {
// 处理逻辑
ajax(url1, () => {
// 处理逻辑
ajax(url2, () => {
// 处理逻辑
})
})
})

为了方便演示,本处采用定时任务的方式进行演示。

需求:每1秒钟输出一次,分别输出:first、second、third、end。

复现回调地狱

let fn = function (order, callback) {
setTimeout(function () {
console.log(order);
callback();
}, 1000);
}

fn("first", function () {
fn("second", function () {
fn("third", function () {
console.log("end");
});
});
});

结果

first
second
third
end

Promise解决回调地狱

let fn = function (order) {
return new Promise(function (resolve, reject) {
setTimeout(function () {
console.log(order);
//异步操作执行完后执行 resolve() 函数
resolve();
}, 1000);
});
}
fn("first")
.then(function () {
//仍然返回一个 Promise 对象
return fn("second");
})
.then(function () {
return fn("third");
})
.then(function () {
console.log('end');
})

结果

first
second
third
end

async + await 解决回调地狱

async + await是对promise的进一步封装。

let fnTimeOut = function (order) {
return new Promise(function (resolve, reject) {
setTimeout(function () {
console.log(order);
//异步操作执行完后执行 resolve() 函数
resolve();
}, 1000);
}
);
};

async function fn() {
let first = await fnTimeOut("first");
let second = await fnTimeOut("second");
let third = await fnTimeOut("third");
console.log("end");
}

fn();

结果

first
second
third
end