像写作一样去写代码,如何把异步的形式改写成同步的形式
写代码的时候,碰到一大堆的缩进、花括号是不是特别头疼?为什么会有这么多的标点符号,还有各种技术概念?能不能像写作一样,自由得书写?从形式上,代码比文章多的是格式,格式代表了对应的技术原理。文本分享一则关于「 同步 、异步、阻塞、非阻塞 」的技术概念,结合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="https://images.unsplash.com/photo-1543363951-ec6198f35a38?auto=format&fit=crop&w=100&q=30";
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="https://images.unsplash.com/photo-1543363951-ec6198f35a38?auto=format&fit=crop&w=100&q=30";
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("https://images.unsplash.com/photo-1543363951-ec6198f35a38?auto=format&fit=crop&w=100&q=30")
.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('https://images.unsplash.com/photo-1543363951-ec6198f35a38?auto=format&fit=crop&w=100&q=30');
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 优雅地写代码
关于MIXLAB
MIXLAB 无界社区是一所面向未来的实验室,它提倡“跨界创新,开放成长”的理念。
——跨界 开放 互助 学习 思维 创新。
目前社区汇集了25000+跨学科人群,主要来自GoogleBrain、微软、华为、阿里鲁班、腾讯、旷视、三角兽、物灵科技、众安保险、美团、360等科技人才、设计师及CEO和投资人;
高校分布MIT、Oxford、Cambridge、CMU、UoM、清华、北大、复旦、上交大、同济等学生及教师群体;
学科跨越机器学习、自然语言处理、量化交易、物联网、区块链、前端、后端、产品经理、UI设计、建筑设计、服装设计、珠宝设计、音乐、艺术等。
详细介绍点击以下卡片:
mixlab期待您的加入!
让你具备无限可能……