在本教程中,您将了解 JavaScript Promise 链,该模式链接 Promise 并按顺序执行异步操作。
JavaScript Promise 链简介
有时,您想执行两个或多个异步的操作,其中下一个操作从上一步的结果开始。例如:
首先,创建一个在 3 秒获得数字 10 的 Promise:
let p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(10);
}, 3 * 100);
});
请注意,这里使用 setTimeout()
函数模拟异步操作。
然后,调用 promise 的 then()
方法:
p.then((result) => {
console.log(result);
return result * 2;
});
一旦 promise 得到解决,传递给 then()
方法的回调就会执行。在回调中,我们显示promise 的结果并返回一个乘以二 result*2
的值。
因为 then()
方法返回一个 Promise
与一个值,所以您可以像这样在返回 Promise
时调用 then()
方法:
let p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(10);
}, 3 * 100);
});
p.then((result) => {
console.log(result);
return result * 2;
}).then((result) => {
console.log(result);
return result * 3;
});
输出:
10
20
在此示例中,第一个 then()
方法中的返回值被传递给第二个 then()
方法。您可以连续调用 then()
方法多次,如下所示:
let p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(10);
}, 3 * 100);
});
p.then((result) => {
console.log(result); // 10
return result * 2;
}).then((result) => {
console.log(result); // 20
return result * 3;
}).then((result) => {
console.log(result); // 60
return result * 4;
});
输出:
10
20
60
我们这样调用 then()
方法的方式通常被称为 Promise 链。
下图说明 Promise 链:
Promise 多个处理程序
当您对一个 Promise 多次调用 then()
方法时,这不是 Promise 链。例如:
let p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(10);
}, 3 * 100);
});
p.then((result) => {
console.log(result); // 10
return result * 2;
})
p.then((result) => {
console.log(result); // 10
return result * 3;
})
p.then((result) => {
console.log(result); // 10
return result * 4;
});
输出:
10
10
10
在这个例子中,我们有一个 Promise 的多个处理程序。这些处理程序没有任何关系。此外,它们独立执行,不会像上面的 Promise 链那样将结果从一个传递到另一个。
下图说明具有多个处理程序的 Promise:
实际上,您很少会为一个 promise 使用多个处理程序。
返回 Promise
当您在 then()
方法中返回一个值时,then()
方法会返回一个 Promise
并立即将返回值解析为新值。
此外,您可以在 then()
方法中返回一个新的 Promise,如下所示:
let p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(10);
}, 3 * 100);
});
p.then((result) => {
console.log(result);
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(result * 2);
}, 3 * 1000);
});
}).then((result) => {
console.log(result);
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(result * 3);
}, 3 * 1000);
});
}).then(result => console.log(result));
输出:
10
20
60
此示例每 3 秒显示 10、20 和 60。此代码模式允许您按顺序执行一些任务。
下面修改上面的例子:
function generateNumber(num) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(num);
}, 3 * 1000);
});
}
generateNumber(10)
.then((result) => {
console.log(result);
return generateNumber(result * 2);
})
.then((result) => {
console.log(result);
return generateNumber(result * 3);
})
.then((result) => console.log(result));
Promise 链接语法
有时,您有多个要按顺序执行异步任务。此外,还需要将上一步的结果传递给下一步。在这种情况下,您可以使用以下语法:
step1()
.then(result => step2(result))
.then(result => step3(result))
...
如果需要先运行上一个任务而不传递结果给下一个任务,则可以使用以下语法:
step1()
.then(step2)
.then(step3)
...
假设你想按顺序执行以下异步操作:
- 首先,从数据库中获取用户。
- 其次,获取所选用户的服务。
- 最后,从用户的服务中计算服务成本。
以下函数说明三个异步操作:
function getUser(userId) {
return new Promise((resolve, reject) => {
console.log('Get the user from the database.');
setTimeout(() => {
resolve({
userId: userId,
username: 'admin'
});
}, 1000);
})
}
function getServices(user) {
return new Promise((resolve, reject) => {
console.log(`Get the services of ${user.username} from the API.`);
setTimeout(() => {
resolve(['Email', 'VPN', 'CDN']);
}, 3 * 1000);
});
}
function getServiceCost(services) {
return new Promise((resolve, reject) => {
console.log(`Calculate the service cost of ${services}.`);
setTimeout(() => {
resolve(services.length * 100);
}, 2 * 1000);
});
}
以下使用 promise 来进行顺序运行任务:
getUser(100)
.then(getServices)
.then(getServiceCost)
.then(console.log);
输出
Get the user from the database.
Get the services of admin from the API.
Calculate the service cost of Email,VPN,CDN.
300
请注意,ES2017 引入async
/await
可帮助您编写比使用 promise 链技术更清晰的代码。
在本教程中,您了解了按顺序执行多个异步任务的 promise 链。