Spring 2019 。 Ric Huang
這樣問其實不精確。JavaScript,如同其他 programming language 一樣,都是同步/blocking 在執行的,但它提供了 non-blocking 的非同步 I/O methods 讓程式在進行 I/O 動作時,得以不會因為 I/O 的速度較慢而影響整體的使用者體驗
// Click when start executing.
// See how the message is printed.
function waitThreeSeconds(){
var ms = 3000 + new Date().getTime();
while(new Date() < ms) {}
console.log("finished function");
}
function clickHandler() {
console.log("click event!");
}
document.addEventListener('click', clickHandler);
console.log("started execution");
waitThreeSeconds();
console.log("finished execution");
=> JavaScript executes synchronously.
有沒有看起來不一樣?
document.addEventListener('click', clickHandler);
console.log("started execution");
waitThreeSeconds();
console.log("finished execution");
function waitThreeSeconds(){
var ms = 3000 + new Date().getTime();
while(new Date() < ms) {}
console.log("finished function");
}
function clickHandler() {
console.log("click event!");
}
let url = 'https://some.web.address.com';
let error, response, result;
console.log("start execution");
const result = someAsyncFunction(url, callbackFunction);
console.log("someAsyncFunction is called");
// When "someAsyncFunction()" is called,
// "callbackFunction()" is put in an "event loop"
// and gets executed once someAsyncFunction is done
function callbackFunction(error, response) {
if (error) { /* handle error here */ }
else { /* put some info in the response */ }
}
Keep your code shallow
Modularize your code
Handle every single error
var form = document.querySelector('form');
form.onsubmit = function (submitEvent) {
var name = document.querySelector('input').value;
request({
uri: "http://example.com/upload",
body: name,
method: "POST"
}, function (err, response, body) {
var statusMessage =
document.querySelector('.status');
if (err) return (statusMessage.value = err);
statusMessage.value = body;
}
)
}
document.querySelector('form').onsubmit = formSubmit
function formSubmit (submitEvent) {
var name = document.querySelector('input').value
request({
uri: "http://example.com/upload",
body: name,
method: "POST"
}, postResponse)
}
function postResponse (err, response, body) {
var statusMessage = document.querySelector('.status')
if (err) return statusMessage.value = err
statusMessage.value = body
}
var formUploader = require('formuploader');
document.querySelector('form').onsubmit =
formUploader.submit;
// “formuploader.js” is where the modules are defined.
module.exports.submit = formSubmit
function formSubmit (submitEvent) {
var name = document.querySelector('input').value
request({
uri: "http://example.com/upload",
body: name,
method: "POST"
}, postResponse)
}
function postResponse (err, response, body) {
var statusMessage = document.querySelector('.status')
if (err) return statusMessage.value = err
statusMessage.value = body
}
Note: When dealing with callbacks, you are by definition dealing with tasks that get dispatched, go off and do something in the background, and then complete successfully or abort due to failure.
What happens if any of them encounters errors? What happens if you don’t handle the error and continue executing the callback functions?
// Important: Apply the Node.js style where the first argument to the callback is always reserved for an error.
var fs = require('fs');
fs.readFile('/Does/not/exist', handleFile);
function handleFile (error, file) {
if (error)
return console.error('Uhoh, there was an error', error);
// otherwise, continue on and use `file` in your code
}
傳統用 callback 來作為 asynchronous functions 的回應方法很容易讓 code 變得很混亂、破碎,且不容易將不同非同步事件之間的關係描述得很乾淨
“Promise” 就是為了讓在 JavaScript 裡頭的 asynchronous 可以 handle 的比較乾淨
Promise 物件:定義好一個 asynchronous function 執行之後成功或是失敗的狀態處理
使用情境:將 Promise 物件的 .then(), .catch() 加入此 asynchronous function 應該要被呼叫的地方
Note: ES6 已經把 promise.js 納入標準
var askMom = function willIGetNewPhone() { … }
askMom(); // execution will be blocked for a week...
var isMomHappy = true;
var willIGetNewPhone
= new Promise(function (resolve, reject) {
if (isMomHappy) {
var phone = { id: 123 }; // your dream phone
resolve(phone);
} else {
var reason = new Error('Mom is unhappy');
reject(reason);
}
});
var askMom = function () {
willIGetNewPhone
.then(function(fullfilled) { …}) // fullfilled = phone
.catch(function(error) { …}); // error = reason
};
askMom();
const handleResolved = (data) => {...}
const handleRejected = (err) => {...}
let myPromise = new Promise((resolve, reject) => {
try {
const data = getData();
resolve(data);
} catch (err) {
reject(err);
}
});
myPromise
.then(handleResolved(data))
.catch(handleRejected(err));
doSomething()
.then(result => doSomethingElse(result))
.then(newResult => doThirdThing(newResult))
.then(finalResult => {...})
.catch(failureCallback);
new Promise((resolve, reject) => {
console.log('Initial');
resolve();
}).then(() => {
throw new Error('Something failed');
console.log('Do this');
}).catch(() => {
console.log('Do that');
}).then(() => {
console.log('Do this whatever happened before');
});
Initial
Do that
Do this whatever happened before
const p1 = new Promise((resolve, reject) => {
setTimeout(resolve, 500, "one");
});
const p2 = new Promise((resolve, reject) => {
setTimeout(resolve, 100, "two");
});
Promise.all([p1, p2]).then((value) => {
console.log(value);
});
const p1 = new Promise((resolve, reject) => {
setTimeout(resolve, 500, "one");
});
const p2 = new Promise((resolve, reject) => {
setTimeout(resolve, 100, "two");
});
Promise.race([p1, p2]).then((value) => {
console.log(value); // Both resolve, but p2 is faster
});
function reqListener () { console.log(this.responseText); }
var oReq = new XMLHttpRequest();
oReq.addEventListener("load", reqListener);
oReq.open("GET", "http://www.example.org/example.txt");
oReq.send();
fetch("https://www.google.com")
.then((res)=>console.log(res.status))
.catch((err) => console.log('Error :', err));
// output: 200
fetch("https://www.google.com")
.then((res)=>console.log(res.text()))
.catch((err) => console.log('Error :', err));
// output a Promise()
// fetch(url, object)
fetch(url, {
method: 'post',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
name: 'Hubot',
login: 'hubot'
})
});
// method: 可以是 get, post, put, delete
// headers: HTTP request header
// body: JSON.stringify 轉成 JSON 字串
Doctor.findById()
.then(doctor => {
// some logic
doctor...
return somePromise;
}).then(...);
const f = async () => {
const doctor = await Doctor.findById();
// some logic
doctor...
}
f();
async function name([param[, param[, ... param]]]) { statements }
async function name([param[, param[, ... param]]]) {
let ret = await doSomethingTakesTime();
// this part does not run until await finishes
}
Await sequentially
Await parallelly