异步编程的概念
学习到小程序缓存机制与异步API时突然对异步、Promise很茫然,电气出身的我脑子里只有异步电机之类的,停下来开始搜索。
在日常web访问时,有时候会遇到光圈,意味着要等待着某些操作完成,对操作系统和java接触过的同学估计会想到多线程来改善,很遗憾JavaScript是单线程的,只能通过异步代码使程序一次做多件事情来实现。
JavaScript异步编程有两种风格,老派的异步callbacks(回调),新派的promise。
异步回调函数
要注意回调不等同于异步,也有回调函数是同步的。区别在于同步的回调函数无需等待任何事情,立即执行。
const gods = ['Apollo', 'Artemis', 'Ares', 'Zeus'];
gods.forEach(function (eachName, index){
console.log(index + '. ' + eachName);
});
然而异步回调是需要等待的,从而规避类似于加载图片未完成时就执行展示图片的代码导致的显示错误。
那么问题来了,什么是回调函数?
就是把函数作为参数传进另一个函数,它将在父函数完成后执行。
异步回调:异步回调是在调用将在后台开始执行代码的函数时指定为参数的函数。当后台代码完成运行时,它将调用回调函数以使您知道工作已完成,或者使您知道发生了一些有趣的事情。
下面展示一个MDN上第一个例子并谈谈我的的通俗理解,有误希望指正。
异步callbacks
btn.addEventListener('click', () => {
alert('You clicked me!');
let pElem = document.createElement('p');
pElem.textContent = 'This is a newly-added paragraph.';
document.body.appendChild(pElem);
});
这段代码的功能是点击btn时弹出警示框“You clicked me”。
异步回调是 () => {
alert(‘You clicked me!’);
let pElem = document.createElement(‘p’);
pElem.textContent = ‘This is a newly-added paragraph.’;
document.body.appendChild(pElem);
}
这个箭头函数,等待前面click完成后执行。
Promise 与 async/await的用法简介
首先来推荐大家一个简洁的视频,11分钟。
Promise与async/await的用法简介
本质上,Promise 是一个对象,代表操作的中间状态 —— 正如它的单词含义 ‘承诺’ ,它保证在未来可能返回某种结果。虽然 Promise 并不保证操作在何时完成并返回结果,但是它保证当结果可用时,你的代码能正确处理结果,当结果不可用时,你的代码同样会被执行,来优雅的处理错误。from MDN
如何使用Promise from MDN
一个 Promise 对象代表一个在这个 promise 被创建出来时不一定已知的值。它让您能够把异步操作最终的成功返回值或者失败原因和相应的处理程序关联起来。 这样使得异步方法可以像同步方法那样返回值:异步方法并不会立即返回最终的值,而是会返回一个 promise,以便在未来某个时候把值交给使用者。
一个 Promise 必然处于以下几种状态之一:
待定(pending): 初始状态,既没有被兑现,也没有被拒绝。
已兑现(fulfilled): 意味着操作成功完成。
已拒绝(rejected): 意味着操作失败。
待定状态的 Promise 对象要么会通过一个值被兑现(fulfilled),要么会通过一个原因(错误)被拒绝(rejected)。当这些情况之一发生时,我们用 promise 的 then 方法排列起来的相关处理程序就会被调用。如果 promise 在一个相应的处理程序被绑定时就已经被兑现或被拒绝了,那么这个处理程序就会被调用,因此在完成异步操作和绑定处理方法之间不会存在竞争状态。
因为 Promise.prototype.then 和 Promise.prototype.catch 方法返回的是 promise, 所以它们可以被链式调用。
demo1
const imagAddress = 'https://miro.medium.com/max/9999/1*_BIT2ZAtovZX-XDIkysxrQ.png '
const imgPromise = ( url ) => {
return new Promise((resolve, reject) => {
const img = new Image();
img.src = url;
img.onload = () => {
resolve( img );
};
img.onerror = () => {
reject(new Error ('图片有误'));
};
});
};
imgPromise( imgAddress )
.then( img =>{
document.body.appendChild(img);
})
.catch(img => {
doucument.body.innerHTML = err;
})
Promise 最大的好处是解决了回调地狱问题,而且因为有了resolve和reject可以进行异步处理得知任务进度
demo2
new Promise((resolve, reject) => {
setTimeout(function(){reject(123)}, 1000)
}).then(res => console.log(res)).catch(e => console.log(e))
async await语法糖
当且仅当async修饰函数后,函数内部可以使用await
await会拿到resolve结果,是then函数的语法糖。