JS 的 Promise 看了好几次了,大概清楚概念,但是一直心里没底,也没写过代码,今天把这些心虚的部分一波带走。
Background
简单聊一下背景,众所周知 JavaScript 是以单线程的方式运行的。在某一时刻内只能执行特定的一个任务,并且会阻塞其它任务的执行。对于类似网络请求等耗时的任务,没必要等任务执行完后才继续后面的操作。在这些任务完成前,JavaScript 完全可以往下执行其他操作,当这些耗时的任务完成后则以回调的方式执行相应处理。这些就是 JavaScript 与生俱来的特性:异步与回调。
大概是这个样子:
networkQuery(queryUrl, function(err, data){
if(err){
console.log("error!")
} else {
// handle data
}
})
当然这也带了一个坑,如果你需要执行多个耗时操作,并且他们之间存在执行结果数据之间的依赖,那你不得不把代码写成下面这样。回调地狱。
networkQuery(queryUrl, function(err, data){
if(err){
console.log("First query error!")
} else {
queryFromBackEnd(data.someUrl, function(err, data){
if(err){
console.log("Second query error!")
} else {
queryFromOtherSystem(data.otherUrl, function(err, data){
if(err){
console.log("Error!")
} else {
console.log("Finally get data in Callback Hell")
}
})
}
})
}
})
逻辑简单还好说,稍微复杂一点,第二天根本看不懂自己写了什么。
Promise
解决回调地狱的其中第一种方式就是使用 Promise。
Promise,如字面所说:承诺。
假装是代码:
我承诺(promise)今天要学会使用 Promise。然后(then)等到今天结束的时候,可能实现了承诺(resolve),不过也有可能遇到(catch)没有实现承诺的情况(reject)。
翻译一下:
let learnPromise = new Promise(function(resolve, reject){
// Wait until the end of the day.
// Check result: Do I understand how to use Promise?
let understand = false;
if(understand){
resolve("Absolutely!")
} else {
reject("Not yet")
}
})
learnPromise.then(function(messageFromResolve){
console.log("Understood ? " + messageFromResolve)
}).catch(function(messageFromReject){
console.log("Understood ? " + messageFromReject)
})
// OUTPUT
// Understood ? Not yet
第一行创建了一个 Promise 对象,创建的时候传入了一个函数,在函数内部进行耗时操作。
此外函数接收两个参数,这两个参数都是函数。分别是实现了承诺的后续操作函数 resolve
,和没有实现承诺的后续操作函数 reject
。resolve
和 reject
, 分别对应 代码的下半部分中 Promise 对象在调用 then()
时传入的函数,和调用 catch()
时传入的函数。
注意Promise 对象 learnPromise
的使用方式,then()
和 catch()
, Promise 让编写异步代码稍微看起来像在写同步代码一样。
Multiple Promises
回到最初的例子,回调地狱。如果使用 Promise,则每个 query 方法都不直接使用回调函数,而是返回一个 Promise 对象,调用的时候在每一个 resolve 函数的最后调用下一阶段的 query 函数,返回对应的 Promise 对象(也就是在 then
中传入函数的最后返回一个 Promise 对象),就可以在 then
方法之后继续 调用 then
。
query()
.then( function(){ /* Return a Promise instance */})
.then( function(){ /* Return a Promise instance */})
.then( function(){ /* Return a Promise instance */})
.catch()
就像这样,我们把返回的结果 message
也作为参数传到了下一个 Promise,在最后一个 Promise 的 resolve
中打印。
let networkQuery = function(message){
return new Promise((resolve, reject) => {
let data = "First query result, based on " + message
resolve(data);
})
}
let queryFromBackEnd = function(message){
return new Promise((resolve, reject) => {
let data = "Second query result, based on " + message
resolve(data);
})
}
let queryFromOtherSystem = function(message){
return new Promise((resolve, reject) => {
let data = "Third query result, based on " + message
resolve(data);
})
}
networkQuery("nothing").then(tempData => {
return queryFromBackEnd(tempData)
}).then(data => {
return queryFromOtherSystem(data)
}).then(finalData => {
console.log(finalData)
})
// OUTPUT
// Third query result, based on Second query result, based on First query result, based on nothing
Other condition
当然有的时候我们或许并不关心执行的过程,只关心是否成功的执行完了所有任务,也可以这样使用
Promise.all([firstOperation(), secondOperation(), thirdOperation()]).then(() =>{
console.log("All finished")
// do something else
})
或者只要其中一个执行成功就可以了:
Promise.race([firstOperation(), secondOperation(), thirdOperation()]).then(() =>{
console.log("One of them is finished")
// do something else
})
最后,understand = true
。
好了,今天就到这里了。