05月16, 2021

像写作一样去写代码,如何把异步的形式改写成同步的形式

写代码的时候,碰到一大堆的缩进、花括号是不是特别头疼?为什么会有这么多的标点符号,还有各种技术概念?能不能像写作一样,自由得书写?从形式上,代码比文章多的是格式,格式代表了对应的技术原理。文本分享一则关于「 同步 、异步、阻塞、非阻塞 」的技术概念,结合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 优雅地写代码

本文链接:https://587v5.com/post/xiang-xie-zuo-yi-yang-qu-xie-dai-ma-,-ru-he-ba-yi-bu-de-xing-shi-gai-xie-cheng-tong-bu-de-xing-shi.html

Comments