通常来说,程序都是顺序执行的,同一时刻只能执行一个事件。如果一个函数依赖于另一个函数的结果,它只能等待那个函数结束才能继续执行,从用户角度来说,整个程序只有一个一个的都执行完才能算运行完毕。
如果我们想了解异步,那么我们必然要先了解下什么是同步。
同步JavaScript
const btn = document.querySelector('button');
btn.addEventListener('click', () => {
alert('You clicked me!');
let pElem = document.createElement('p');
pElem.textContent = 'This is a newly-added paragraph.';
document.body.appendChild(pElem);
});
每一个操作在执行的时候,其他任何事情都没有发生 — 网页的渲染暂停,任何时候只能做一件事情, 只有一个主线程,其他的事情都阻塞了,直到前面的操作完成。
异步JavaScript在JavaScript代码中,你经常会遇到两种异步编程风格:老派callbacks,新派promise。
异步callbacks
异步callbacks 其实就是函数,只不过是作为参数传递给那些在后台执行的其他函数. 当那些后台运行的代码结束,就用callbacks函数,通知你工作已经完成,或者其他有趣的事情发生了。遇到两种异步编程风格:老派callbacks,新派promise
例如:
异步callback 就是addEventListener()
btn.addEventListener('click', () => {
alert('You clicked me!');
let pElem = document.createElement('p');
pElem.textContent = 'This is a newly-added paragraph.';
document.body.appendChild(pElem);
});
第一个参数是侦听的事件类型,第二个就是事件发生时调用的回调函数。.
当我们把回调函数作为一个参数传递给另一个函数时,仅仅是把回调函数定义作为参数传递过去 — 回调函数并没有立刻执行,回调函数会在包含它的函数的某个地方异步执行,包含函数负责在合适的时候执行回调函数。
异步PromisesPromise 对象本质上表示的是一系列操作的中间状态,或者说是未来某时刻一个操作完成或失败后返回的结果。Promise并不保证操作在何时完成并返回结果,但是保证在当前操作成功后执行你对操作结果的处理代码
例如:
fetch('products.json').then(function(response) {
return response.json();
}).then(function(json) {
products = json;
initialize();
}).catch(function(err) {
console.log('Fetch problem: ' + err.message);
});
fetch操作目前正在等待浏览器试图在将来某个时候完成该操作的结果。然后我们有三个代码块链接到fetch()的末尾:
- 两个 then() 块。两者都包含一个回调函数,如果前一个操作成功,该函数将运行,并且每个回调都接收前一个成功操作的结果作为输入,因此您可以继续对它执行其他操作。每个 .then()块返回另一个promise,这意味着可以将多个.then()块链接到另一个块上,这样就可以依次执行多个异步操作。
- 如果其中任何一个then()块失败,则在末尾运行catch()块——与同步try…catch类似,catch()提供了一个错误对象,可用来报告发生的错误类型。但是请注意,同步try…catch不能与promise一起工作,尽管它可以与async/await一起工作,稍后您将了解到这一点。
像promise这样的异步操作被放入事件队列中,事件队列在主线程完成处理后运行,这样它们就不会阻止后续JavaScript代码的运行。排队操作将尽快完成,然后将结果返回到JavaScript环境。
Promises 与 callbacks的区别- 您可以使用多个then()操作将多个异步操作链接在一起,并将其中一个操作的结果作为输入传递给下一个操作。这种链接方式对回调来说要难得多,会使回调以混乱的“末日金字塔”(挺迷得末日金字塔???,有够中二)告终。
- Promise总是严格按照它们放置在事件队列中的顺序调用。
- 错误处理要好得多——所有的错误都由块末尾的一个.catch()块处理,而不是在“金字塔”的每一层单独处理。