因为JavaScript属於同步的语言,一次只能作一件事情,遇到非同步的事件就会把该事件挪到最後执行。
console.log("Start!");
setTimeout(() => { console.log("非同步事件") },1000)
console.log("End!")
//
Start!
End!
非同步事件
Ajax也一样属非同步行为,如果要确保撷取到远端资料後,并且针对远端资料作处理的话,那了解promise的运作就非常重要。以下例子为Promise base的Ajax函式库axios为例子:
let data = [];
axios.get('https://randomuser.me/api/').then((res) => {
data = res
console.log(data) // random user data
})
console.log(res) // []
Promise object的构造器语法如下:
let promise = new Promise(function(resolve, reject) {
// executor
});
传递给new Promise的函数被称为executor。当new Promise被创建,executor会自动运行。
当executor获得了结果,会执行以下两个callback:
new Promise构造器返回的promise对象具有以下的内部属性:
举一个promise构造器和一个简单的executor函数:
let promise = new Promise(function(resolve, reject) {
// 当new promise时,自动执行此函数
// 1秒後执行成功,带有value "done"
setTimeout(() => resolve("done"), 1000);
});
经过一秒的处理後,executor调用resolve('done')来产生结果,改变promise object的状态:
reject的例子:
let promise = new Promise(function(resolve, reject) {
// 1 秒后发出工作已经被完成的信号,并带有 error
setTimeout(() => reject(new Error("Whoops!")), 1000);
});
executor只能调用一个resolve或是一个reject。所以再对resolve或是reject的调用都会被忽略。
let promise = new Promise(function(resolve, reject) {
resolve("done");
reject(new Error("…")); // 被忽略
setTimeout(() => resolve("…")); // 被忽略
});
尽量以Error object调用reject
Resolve/reject可以立即进行(无非同步)
实际上executor通常是进行某些非同步操作,并在一段时间後调用resolve/reject,但这不是必须的
let promise = new Promise(function(resolve, reject) {
// 不花时间去做这项工作
resolve(123); // 立即给出结果:123
});
state与result都是内部属性,无法直接获取,但我们可以使用.then/.catch/.finally等方法。
promise.then(
function(result) { /* handle a successful result */ },
function(error) { /* handle an error */ }
);
.then接收两个函数,第一个函数接收promise resolve後的结果,第二个则接收promise reject的结果。
let promise = new Promise(function(resolve, reject) {
setTimeout(() => resolve("done!"), 1000);
});
promise.then(
result => alert(result), // 1秒後显示 "done!"
error => alert(error) // 不运行
);
reject的情况下:
let promise = new Promise(function(resolve, reject) {
setTimeout(() => reject(new Error("Whoops!")), 1000);
});
promise.then(
result => alert(result), // 不运行
error => alert(error) // 1秒後显示 "Error: Whoops!"
);
如果只对成功完成感兴趣,那也可以只提供一个函数。
let promise = new Promise(resolve => {
setTimeout(() => resolve("done!"), 1000);
});
promise.then(alert); // 1秒後显示 "done!"
如果只对error感兴趣,可以使用null作为第一个参数,.then(null,errorHandleFunction),或者使用.catch:
let promise = new Promise((resolve, reject) => {
setTimeout(() => reject(new Error("Whoops!")), 1000);
});
// .catch(f)跟promise.then(null, f)是一样的
promise.catch(alert); // 1秒後显示 "Error: Whoops!"
finally中的callback function是在promise执行resolve或reject後执行,是一个很好执行清理(clean up)或是处理程序(handler),例如无论执行的结果如何都可以在finally中把loading indicator关掉。
new Promise((resolve, reject) => {
// 做一些处理後调用resolve or reject
})
// promise为settled,不管成功与否
.finally(() => stop loading indicator)
// 所以在finally中的处理,都会在我们处理成功/错误结果之前
.then(result => show result, err => show error)
new Promise((resolve, reject) => {
setTimeout(() => resolve("result"), 2000)
})
.finally(() => alert("Promise ready"))
.then(result => alert(result));
或是一起传给catch
new Promise((resolve, reject) => {
throw new Error("error");
})
.finally(() => alert("Promise ready"))
.catch(err => alert(err));
可以在.then()中回传一个promise,那下一个.then()就会等到回传的promise调用resolve或reject:
new Promise((resolve, reject) => {
setTimeout(() => resolve(1), 1000)
}).then((result) => {
console.log(result);
return new Promise((resolve, reject)=> {
setTimeout(() => resolve(2), 2000)
})
}).then((result) => {
console.log(result);
})
通常会在Promise chain中的末端利用.catch()进行错误处理,所以可能会在多个.then()後才出现,如果整个promise chain都没有报错,那.catch()就不会被执行。
直接看一个简单的例子:
function testError() {
return new Promise((resolve, reject) => {
nonExistMethod();
})
}
testError().then((result) => { console.log(result) }).catch((error) => { console.log(`error catch successfully! ${error}`) })
// error catch successfully! ReferenceError: nonExistMethod is not defined
为什麽没有执行reject,error也会被.catch捕抓到呢? 原因是因为Promise内部有一层try..catch所包住,而且catch的部分预设使用reject function。
那如果error是出现在Promise外面呢?
function testError() {
nonExistMethod();
return new Promise((resolve, reject) => {
resolve("result!")
})
}
testError().then((result) => { console.log(result) }).catch((error) => { console.log(`error catch successfully! ${error}`) })
这样的情况下,promise chain的.catch是没有办法捕捉到error的,必须自己利用try..catch来补捉:
function testError() {
nonExistMethod();
return new Promise((resolve, reject) => {
resolve("result!")
})
}
try {
testError().then((result) => { console.log(result) }).catch((error) => { console.log(`error catch successfully! ${error}`) })
} catch(error) {
console.log("handle by outer try-catch")
}
最後介绍Promise的其他方法:
说明这些方法前,先定义一个promise函数,可以传入两个参数:
function ownPromise(count, time = 1000) {
return new Promise((resolve, reject) => {
setTimeout(() => {
count? resolve(`第${count}次成功!`) : reject("失败")
},time)
})
}
以数组的形式传入多个promise函数,结果一样以数组的形式回传。
Promise.all([ownPromise(1), ownPromise(2), ownPromise(3,4000)]).then((res) => { console.log(res) })
// ["第1次成功!", "第2次成功!", "第3次成功!"]
以数组的形式传入多个promise函数,与Promise.all不同的是只会回传单一结果,也就是第一个完成的。
Promise.race([ownPromise(1), ownPromise(2), ownPromise(3,4000)]).then((res) => { console.log(res) })
// 第1次成功!
直接看例子会比较快明白:
let result = Promise.resolve("result");
result.then(res => { console.log(res) }) // result
let rejectResult = Promise.reject("error");
rejectResult.catch(rej => { console.log(rej) }) // error
<<: Day14:14 - 购物车服务(2) - 前端 - 购物车总商品显示、加入购物车
>>: Day13-Kubernetes 那些事 - Deployment 与 ReplicaSet(一)
Socket.io 注意server side需要使用3.0.3版本 否则flutter clien...
雪花随风飘 教学原文参考:雪花随风飘 这篇文章会介绍,如何在 Scratch 3 里使用建立分身、绘...
元件介绍 Card 是一个可以显示单个主题内容及操作的元件,通常这个主题内容包含图片、标题、描述或是...
今天花了一整天Debug,一直看为甚麽「Not Work」,单纯纪录一下流程。 今日目标 修好昨天的...
前言 这是 Obsidian 使用教学 — 基础篇的第 7 篇文章。 在 上一篇文章 中,我分享了个...