写代码的时候,碰到一大堆的缩进、花括号是不是特别头疼?为什么会有这么多的标点符号,还有各种技术概念?能不能像写作一样,自由得书写?从形式上,代码比文章多的是格式,格式代表了对应的技术原理。文本分享一则关于「 同步 、异步、阻塞、非阻塞 」的技术概念,结合Javascript中的图片加载,介绍如何把异步的形式改写成同步的形式,更加优雅的书写我们的代码。
def CodeFun( ):
先来看这么一个故事:
领导M需要准备一份年终总结的PPT,
他把这件事安排给了下属L
故事1 领导M非常不放心L,
于是决定在L边上陪着他把PPT做完
技术原理::「 同步阻塞 」
旁白::这领导太低效了,自己动手不行吗?
故事2 M安排L做PPT后,
跑去会议室开会,
并时不时到下属L的工位前看看PPT做完没
技术原理::「 同步非阻塞 」
旁白::这是大部分领导的做法(显得自己很忙)
故事3 M在交代PPT任务时,
特定嘱咐L,
做完PPT要主动来找他。
但是M还是不放心,
决定等在L边上,
陪着他做PPT
技术原理::「 异步阻塞 」
旁白::有这样的下属,也是够操心的。
故事4 M在交代PPT任务时,
特定嘱咐L,
做完PPT要主动来找他。
此时,
M决定全权交给L负责,
自己继续在会议室开会
技术原理::「 异步非阻塞 」
旁白::下属L发挥自己的主动性,把PPT完美完成,领导M也完成了会议。
所谓同步异步,只是对于L而言:
L做完PPT后沉默不语,叫 「 同步 」;
L做完PPT主动汇报,叫「 异步 」。
「 同步 」 的情况下,M得自己主动去询问做完PPT没。
「 异步 」 的情况下,M可以忙自己的事,L做完PPT会主动汇报。
所谓阻塞非阻塞,仅仅对于M而言:
「 阻塞 」的情况下, M陪着L做PPT。
「 非阻塞 」的情况下,M去会议室开会。
显然,「 异步+非阻塞 」是最高效的。
这就是同步、异步、阻塞、非阻塞的概念的通俗理解。回到代码写作上,我们实际写代码的时候,会比较习惯一种「 线性思维 」的方式,这种方式有点类似于做数学证明题的过程:
因为等边△ABC 所以∠A=∠B=60度 又PE⊥AC 所以∠AEP是直角 所以∠APE=30度 在△PBQ中 ∠B=60度,∠Q=28度 所以∠QPB=92度 所以∠EPD=180-92-30=58度
一行行的书写方式,逻辑性非常强,简单明了的因果关系 ,这是一种典型的线性思维。下面举一个Javascript的例子。初学JS的同学,为了把图片绘制到canvas上,一般会这么写:
var img=.....
....
ctx.drawImage(img,0,0);
我们初学的时候,习惯一行行的书写方式,把图片数据存储在一个变量img里,然后再调用绘图命令使用img。
我们可以在浏览器中打开「 开发者工具 」,在console面板中进行实验:
var img=new Image();
img.src="xxxxxx";
var canvas=document.createElement("canvas");
var ctx=canvas.getContext("2d");
ctx.drawImage(img,0,0);
document.body.innerHTML='';
document.body.appendChild(canvas);
运行下,如果按照以上的写法,经常会出现图片绘制不出来的情况,因为图片是「 异步 」加载的。这个时候,我们需要把代码改写下:
var img=new Image();
img.src="xxxxxx";
img.onload=function(){
var canvas=document.createElement("canvas");
var ctx=canvas.getContext("2d");
ctx.drawImage(img,0,0);
document.body.innerHTML='';
document.body.appendChild(canvas);
};
代码开始有了缩进,我们需要了解「 作用域 」的概念,我们继续把图片加载写成一个函数:
function loadImg(_url,_callback){
var img=new Image();
img.src=_url;
img.onload=function(){
_callback(img);
};
};
加载一张图片:
loadImg("http://xxxx",function(img){
...
});
如果是多张图片加载呢?
loadImg("http://xx1",function(img){
loadImg("http://xx2",function(img){
loadImg("http://xx3",function(img){
loadImg("http://xx4",function(img){
...
});
});
});
});
一层层的嵌套,写起来,看起来都非常难受 ,为了解决这个问题,Promise出现了,loadImg可以写出then这种方式:
loadImg("http://xxx")
.then(function(img){
....
});
多张图片的加载,变成了这样:
loadImg("http://xxx1")
.then(function(img){
....
});
loadImg("http://xxx2")
.then(function(img){
....
});
loadImg("http://xxx3")
.then(function(img){
....
});
是不是开始有点一行行在书写代码的感觉?但是还是有一个缩进在碍事。以上例子真实可运行的代码可以参考如下:
function loadImg(_url){
var img=new Image();
img.src=_url;
return new Promise(function(resolve, reject){
img.onload=function(){
resolve(img);
};
});
};
loadImg("xxxx")
.then(function(img){
var canvas=document.createElement("canvas");
var ctx=canvas.getContext("2d");
ctx.drawImage(img,0,0);
document.body.innerHTML='';
document.body.appendChild(canvas);
});
那能不能再简洁点呢?有个then在后面跟着看着也不太爽。而且我们在追求一行行的书写方式。这个时候就要用到 async/await 了,我们改写下:
async function loadImg(_url){
var img=new Image();
img.src=_url;
return new Promise(function(resolve, reject){
img.onload=function(){
resolve(img);
};
});
};
var img=await loadImg('xxx');
var canvas=document.createElement("canvas");
var ctx=canvas.getContext("2d");
ctx.drawImage(img,0,0);
document.body.innerHTML='';
document.body.appendChild(canvas);
这下加载图片,可以用我们熟悉的“一行行”的写作方式了:
var img=await ...
....
ctx.drawImage(img,0,0);
return 优雅地写代码
Comments