Promise
(Promise Chain
)Callback Hell
, bằng cách xử lý gọi lệnh từng dòng chạy song song, dễ nhìn và dễ chỉnh sửa.var newPromise = new Promise((resolve, reject) => {
resolve('Resolve');
});
newPromise
.then((result) => console.log('Successfully: ', result))
.catch((error) => console.error(error + ''));
handle
của Promise với nhiều khối lệnh .then
liên tiếpvar newPromise = new Promise((resolve, reject) => {
resolve('Success!');
});
newPromise
.then(() => console.log(1)) // Line 1
.then(() => console.log(2)) // Line 2
.then(() => console.log(3)) // Line 3
.catch((error) => console.error(error + ''))
.finally(() => console.log('Done!'));
.then
thứ nhất, sẽ chạy tiếp .then
thứ 2 rồi mới chạy tiếp .then
thứ ba.handle
của Promise với nhiều khối lệnh .then
liên tiếp, trong đó có xử lý setTimeOut
để kiểm tra các .then
là xử lý bất đồng bộ hay đồng bộ.var newPromise = new Promise((resolve, reject) => {
resolve('Success!');
});
newPromise
.then(() => console.log(1)) // Line 1
.then(() => {
setTimeout(() => {
console.log(2); // Line 2
}, 1000);
})
.then(() => console.log(3)) // Line 3
.catch(() => console.error('Error!'))
.finally(() => console.log('Done!'));
.then
thì xử lý ở đây vẫn là bất đồng bộ không theo thứ tự từng khối line..then
trong chuỗi (Promise Chain)var newPromise = new Promise((resolve, reject) => {
resolve();
});
//prettier-ignore
newPromise
.then(function () {
console.log('Line1'); // Line 1
})
.then(function (data) {
console.log('Line2 : ', data); // Line 2
return 2
})
.then(function (data) {
console.log('Line3 : ', data); // Line 3
})
.catch(() => console.error('Error!'))
.finally(() => console.log('Done!'));
.then
phía trước nếu không return một giá trị thì tham số data của khối lệnh .then
trong callback
kế tiếp sẽ là giá trị Undefined
var newPromise = new Promise((resolve, reject) => {
resolve('Success!');
});
newPromise
.then((rs) => {
console.log(rs); // line Success
return 1;
})
.then((line2) => {
console.log(line2); // line 1
return 2;
})
.then((line3) => {
console.log(line3); // line 2
return 3;
})
.then((line4) => {
console.log(line4); // line 3
})
.catch(() => console.error('Error!'))
.finally(() => console.log('Done!'));
return
của khối lệnh .then
callback
phía trước sẽ trở thành tham số của khối lệnh .then
trong callback
phía sau..then
liên tiếp có return
giá trị, và có setTimeOut
để kiểm tra kế thừa giá trị và bất đồng bộ hay đồng bộ.var newPromise = new Promise((resolve, reject) => {
resolve('Success!');
});
newPromise
.then((rs) => {
setTimeout(() => {
console.log('Line 1: ', rs); // line 1
return 1;
}, 4000);
})
.then((line2) => {
setTimeout(() => {
console.log('Line 2: ', line2); // line 2
return 2;
}, 3000);
})
.then((line3) => {
setTimeout(() => {
console.log('Line 3: ', line3); // line 3
return 3;
}, 2000);
})
.then((line4) => {
setTimeout(() => {
console.log('Line 4: ', line4); // line 4
}, 1000);
})
.catch(() => console.error('Error!'))
.finally(() => console.log('Done!'));
return
một Promise
thì sẽ chạy xử lý tiếp ở .then
kế tiếp mà không chờ xử lý.var newPromise = new Promise((resolve, reject) => {
resolve('Success!');
});
newPromise
.then((rs) => {
setTimeout(() => {
return 1;
}, 4000);
})
.then((data) => {
setTimeout(() => {
console.log('Line 2: ', data); // line 2
return 2;
}, 3000);
})
.then((data) => {
setTimeout(() => {
console.log('Line 3: ', data); // line 3
return 3;
}, 2000);
})
.then((data) => {
setTimeout(() => {
console.log('Line 4: ', data); // line 4
}, 1000);
})
.catch(() => console.error('Error!'))
.finally(() => console.log('Done!'));
.then
này vẫn chạy theo tính chất đồng bộ và nếu không chạy xử lý .then
phía trước không return
đối tượng Promise.var newPromise = new Promise((resolve, reject) => {
resolve('Success!');
});
newPromise
.then((data) => {
setTimeout(() => {
return new Promise((resolve) => {
console.log('Line 1: ', data); // Line 1
resolve(2);
});
}, 2000);
})
.then((data) => {
setTimeout(() => {
return new Promise((resolve) => {
console.log('Line 2: ', data); // Line 2
resolve(3);
});
}, 1000);
})
.then((data) => {
console.log('Line 3: ', data); // Line 3
})
.catch(() => console.error('Error!'))
.finally(() => console.log('Done!'));
Ta thấy các khối lệnh .then
do chạy setTimeout
trước nên bị lướt qua chạy bất đồng bộ, sau đó chạy tiếp các .then
kế tiếp mà không chờ xử lý Promise trong khối lệnh setTimeout
Như vậy nếu khối lệnh .then
không xử lý return
trả về một đối tượng Promise thì vẫn chạy theo kiểu đồng bộ chạy tiếp câu lệnh kế tiếp, nếu có xử lý setTimeOut
thì sẽ chạy ngầm và chạy tiếp xử lý ở .then kế tiếp mà không dừng lại chờ.
var newPromise = new Promise((resolve, reject) => {
resolve('Success!');
});
newPromise
.then((data) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('Line 1: ', data); // Line 1
resolve(1);
}, 3000);
});
})
.then((data) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('Line 2: ', data); // Line 2
resolve(2);
}, 2000);
});
})
.then((data) => {
console.log('Line 3: ', data); // Line 3
})
.catch(() => console.error('Error!'))
.finally(() => console.log('Done!'));
.then
xử lý return
trả về một Promise thì các khối lệnh .then
chạy theo xử lý Đồng bộ theo thứ tự và chờ xử lý xong câu lệnh .then
phía trước mới chạy tiếp các khối lệnh .then
kế tiếp.var newPromise = new Promise((resolve, reject) => {
resolve();
});
//prettier-ignore
newPromise
.then(function () {
return new Promise((resolve) => {
console.log('Line1'); // Line 1
setTimeout(resolve,3000)
})
})
.then(function (data) {
console.log('Line2'); // Line 2
return 2
})
.then(function (data) {
console.log('Line3 : ', data); // Line 3
})
.catch(() => console.error('Error!'))
.finally(() => console.log('Done!'));
Reject()
để kiểm tra kết quả trả về.var newPromise = new Promise((resolve, reject) => {
resolve();
});
//prettier-ignore
newPromise
.then(function () {
return new Promise((resolve) => {
console.log('Line1'); // Line 1
setTimeout(() => {
resolve(2);
},3000)
})
})
.then(function (data) {
return new Promise((resolve, reject) => {
console.log('Line2: ', data); // Line 2
setTimeout(() => {
reject("Error1") // Rejected 1
},2000)
})
})
.then(function (data) {
return new Promise((resolve) => {
console.log('Line3: ', data); // Line3
setTimeout(() => {
resolve(3) // Resolve
},2000)
})
})
.catch((error) => console.error('Error 1: ', error))
.then(function (data) {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('Line4: ', data); // Line 4
reject("Error1") // Rejected 2
},2000)
})
})
.catch((error) => console.error('Error: ',error)) // Rejected Catch
.finally(() => console.log('Done!'));
Reject()
của .then
thì sẽ tìm khối lệnh .catch
gần nhất để xử lý, sau đó chạy tiếp khối lệnh .then
phía sau của .catch
var newPromise = new Promise((resolve, reject) => {
resolve();
});
//prettier-ignore
newPromise
.then(function () {
return new Promise((resolve) => {
console.log('Line1'); // Line 1
setTimeout(() => {
resolve(2);
},3000)
})
})
.then(function (data) {
return new Promise((resolve, reject) => {
console.log('Line2: ', data); // Line 2
setTimeout(() => {
reject("Error1") // Rejected 1
},2000)
})
})
.then(function (data) {
return new Promise((resolve) => {
console.log('Line3: ', data); // Line 3
setTimeout(() => {
resolve(3)
},2000)
})
})
.then(function (data) {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('Line4: ', data); // Line 4
reject("Error1")
},2000)
})
})
.catch((error) => console.error('Error: ',error))
.finally(() => console.log('Done!'));
Reject()
thì sẽ nhảy đến xử lý ở khối lệnh .catch
gần nhất, nếu sau khối lệnh .catch
không có khối lệnh .then
thì xử lý handle
đối tượng newPromise
sẽ được dừng lại..then
khi trả về đối tượng Promise thì có thể dùng chung một khối lệnh .catch
và từ khối lệnh .then
được return
sẽ không chạy xử lý tiếp các khối lệnh .then
còn lại, như vậy chuỗi .then
hay tính chất chuỗi của Promise Chain sẽ được dừng lại khi gặp xử lý .then
return
trả về một đối tượng Promise bị Rejected
thì sẽ dừng lại không xử lý tiếp các khối lệnh .then
tiếp theo phía sau.Bài tập: In ra màn hình các số theo thứ tự sau mỗi giây, không dùng setInterval.
function sleep(ms) {
return new Promise(function (resolve) {
setTimeout(() => {
return resolve();
}, ms);
});
}
function sleep(ms) {
return new Promise(function (resolve) {
setTimeout(resolve, ms);
});
}
sleep(1000)
.then(function (data) {
console.log(1);
return sleep(1000);
})
.then(function (data) {
console.log(2);
return sleep(1000);
})
.then(function (data) {
console.log(3);
return sleep(1000);
})
.then(function (data) {
console.log(4);
return sleep(1000);
})
.then(function (data) {
console.log(5);
return sleep(1000);
})
.catch((error) => console.error('Error: ', error))
.finally(() => console.log('Done!'));
function sleep(ms, data) {
return new Promise(function (resolve) {
setTimeout(() => resolve(++data), ms);
});
}
sleep(1000, 0)
.then(function (data) {
console.log(data);
return sleep(1000, data);
})
.then(function (data) {
console.log(data);
return sleep(1000, data);
})
.then(function (data) {
console.log(data);
return sleep(1000, data);
})
.then(function (data) {
console.log(data);
return sleep(1000, data);
})
.then(function (data) {
console.log(data);
return sleep(1000, data);
})
.then(function (data) {
console.log(data);
return sleep(1000, data);
})
.catch((error) => console.error('Error: ', error))
.finally(() => console.log('Done!'));
Arrow Function
sẽ viết gọn lại như sau:function printLog(ms, count) {
return new Promise((resolve, reject) => {
setTimeout(() => {
count++;
console.log(count);
return resolve(count);
}, ms);
});
}
printLog(1000, 0)
.then((data) => printLog(1000, data))
.then((data) => printLog(1000, data))
.then((data) => printLog(1000, data))
.then((data) => printLog(1000, data))
.then((data) => printLog(1000, data))
.then((data) => printLog(1000, data))
.then((data) => printLog(1000, data))
.then((data) => printLog(1000, data))
.then((data) => printLog(1000, data))
.catch((error) => console.error('Error: ', error))
.finally(() => console.log('Done!'));