贡献者: addis
由于 JS 主要是单线程操作,所以像文件读写、上传下载等操作都必须异步完成。
function loadXMLDoc(url) {
let xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
// Here you can handle the XML data
let xmlDoc = this.responseXML;
console.log(xmlDoc);
}
};
xmlhttp.open("GET", url, true);
xmlhttp.send();
}
loadXMLDoc(myURL);
// 其他命令
当此时浏览器会单独开一个线程用于下载 url
中的文件,但在下载完以前 loadXMLDoc(myURL);
语句就会结束然后开始 其他命令
。只有 其他命令
全部都执行完了,才会开始 event loop,如果下载已经完成,就会把这个事件放到执行队列里面。其他命令
执行完了下载还没完成,就会一直做 event loop 检查事件,如果发生了别的事件(例如用户按下按钮)就会先去处理该事件,这样就不会出现等到下载完页面才有反应的尴尬现象。
所以可能一些从来没见过异步编程的程序员很不习惯的地方就在于 其他命令
中是不可能获取到下载的任何数据的。下载的数据只能在回调函数 xmlhttp.onreadystatechange
中处理!
这就是事件导向的编程思路!
console.log("Before Promise");
let myPromise = new Promise((resolve, reject) => {
console.log("Inside Promise executor");
resolve("Operation successful");
});
myPromise.then(
(value) => { console.log(value); }, // Executed asynchronously
(error) => { console.log(error); }
);
console.log("After Promise");
输出:
Before Promise
Inside Promise executor
After Promise
Operation successful
Promise
是内建类型,生成时会马上执行第一个 arg 提供的函数(叫做 Promise executor),resolve, reject
函数也同样由系统提供,用于承诺完成或出错时告知系统。在这个例子中,在生成器中,resolve()
马上被调用了。
myPromise
立即被 resolve
成了一个字符串 "Operation successful"
。
resolve, reject
也可以延迟调用,例如:
let myPromise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('Completed!');
}, milliseconds);
});
setTimeout
马上会执行完毕(浏览器会使用另一个线程打开一个计时器),然后 myPromise
就处于一个没有 resolve 的状态。
myPromise.then(handler1, handler2)
。这个命令并不会立即执行 handler1, handler2
,只是告诉 myPromise
应该怎么处理 resolve 后的结果。该命令后面的东西会马上继续运行直到结束。注意即使 Promise executor
执行完了,也需要等到后面的所有代码运行结束了,进入 event loop 才能执行 handler。
new Promise
语句中,是否立即调用该了 resolve
或 reject
,handler 都会在当前所有代码执行完以后,且承诺 resolve 了以后才执行。
.then()
方法返回什么呢?返回一个新的承诺,resolve 到 handler
返回的值! 于是这个新的 promise 等到完成后又可以被另一个 .then()
处理。
.then()
都没有 handler2
,那么系统会提供默认的,它的行为就是把错误信息传给新生成的 promise,并立即调用 reject()
把这个信息放进去,不断传给新的 .then()
生成的 promise,直到遇到 .catch(handler2)
,用用户提供的 handler2
来处理这个错误。