ThinkPHP5 框架引入 Go AOP,PHP AOP编程项目详解

本文实例讲述了ThinkPHP5 框架引入 Go AOP,PHP AOP编程。分享给大家供大家参考,具体如下:

项目背景

目前开发的WEB软件里有这一个功能,PHP访问API操作数据仓库,刚开始数据仓库小,没发现问题,随着数据越来越多,调用API时常超时(60s)。于是决定采用异步请求,改为60s能返回数据则返回,不能则返回一个异步ID,然后轮询是否完成统计任务。由于项目紧,人手不足,必须以最小的代价解决当前问题。

方案选择

  1. 重新分析需求,并改进代码
  2. 采用AOP方式改动程序

从新做需求分析,以及详细设计,并改动代码,需要产品,架构,前端,后端的支持。会惊动的人过多,在资源紧张的情况下是不推荐的。
采用AOP方式,不改动原有代码逻辑,只需要后端就能完成大部分任务了。后端用AOP切入请求API的方法,通过监听API返回的结果来控制是否让其继续运行原有的逻辑(API在60s返回了数据),或者是进入离线任务功能(API报告统计任务不能在60s内完成)。

之前用过AOP-PHP拓展,上手很简单,不过后来在某一个大项目中引入该拓展后,直接爆了out of memory,然后就研究其源码发现,它改变了语法树,并Hook了每个被调用的方法,也就是每个方法被调用是都会去询问AOP-PHP,这个方法有没有切面方法。所以效率损失是比较大的。而且这个项目距离现在已经有8年没更新了。所以不推荐该解决方案。

实际环境

Debian,php-fpm-7.0,ThinkPHP-5.10。

引入AOP

作为一门zui好的语言,PHP是不自带AOP的。那就得安装AOP-PHP拓展,当我打开pecl要下载时,傻眼了,全是bate版,没有显示说明支持php7。但我还是抱着侥幸心理,找到了git,发现4-5年没更新了,要不要等一波更新,哦,作者在issue里说了有时间就开始兼容php7。
好吧,狠话不多说,下一个方案:Go!AOP.看了下git,作者是个穿白体恤,喜欢山峰的大帅哥,基本每个issue都会很热心回复。

composer require goaop/framework

ThinkPHP5 对composer兼容挺不错的哦,(到后面,我真想揍ThinkPHP5作者)这就装好了,怎么用啊,git上的提示了简单用法。我也就照着写了个去切入controller。

<?PHP
namespace app\tests\controller;

use think\Controller;

class Test1 extends Controller
{
 public function test1()
 {
  echo $this->aspectAction();
 }

 public function aspectAction()
 {
  return 'hello';
 }
}

定义aspect

<?PHP
namespace app\tests\aspect;

use Go\Aop\Aspect;
use Go\Aop\Intercept\FieldAccess;
use Go\Aop\Intercept\MethodInvocation;
use Go\Lang\Annotation\After;
use Go\Lang\Annotation\Before;
use Go\Lang\Annotation\Around;
use Go\Lang\Annotation\Pointcut;

use app\tests\controller\Test1;

class MonitorAspect implements Aspect
{

 /**
  * Method that will be called before real method
  *
  * @param MethodInvocation $invocation Invocation
  * @Before("execution(public|protected app\tests\controller\Test1->aspectAction(*))")
  */
 public function beforeMethodExecution(MethodInvocation $invocation)
 {
  $obj = $invocation->getThis();
  echo 'Calling Before Interceptor for method: ',
    is_object($obj) ? get_class($obj) : $obj,
    $invocation->getMethod()->isStatic() ? '::' : '->',
    $invocation->getMethod()->getName(),
    '()',
    ' with arguments: ',
    json_encode($invocation->getArguments()),
    "<br>\n";
 }
}

启用aspect

<?PHP
// file: ./application/tests/service/ApplicationAspectKernel.php

namespace app\tests\service;

use Go\Core\AspectKernel;
use Go\Core\AspectContainer;

use app\tests\aspect\MonitorAspect;

/**
 * Application Aspect Kernel
 *
 * Class ApplicationAspectKernel
 * @package app\tests\service
 */
class ApplicationAspectKernel extends AspectKernel
{

 /**
  * Configure an AspectContainer with advisors, aspects and pointcuts
  *
  * @param AspectContainer $container
  *
  * @return void
  */
 protected function configureAop(AspectContainer $container)
 {
  $container->registerAspect(new MonitorAspect());
 }
}

go-aop 核心服务配置

<?PHP
// file: ./application/tests/behavior/Bootstrap.php
namespace app\tests\behavior;

use think\Exception;
use Composer\Autoload\ClassLoader;
use Go\Instrument\Transformer\FilterInjectorTransformer;
use Go\Instrument\ClassLoading\AopComposerLoader;
use Doctrine\Common\Annotations\AnnotationRegistry;

use app\tests\service\ApplicationAspectKernel;
use app\tests\ThinkPhpLoaderWrapper;

class Bootstrap
{
 public function moduleInit(&$params)
 {
  $applicationAspectKernel = ApplicationAspectKernel::getInstance();
  $applicationAspectKernel->init([
   'debug' => true,
   'appDir' => __DIR__ . './../../../',
    'cacheDir' => __DIR__ . './../../../runtime/aop_cache',
    'includePaths' => [
     __DIR__ . './../../tests/controller',
     __DIR__ . './../../../thinkphp/library/think/model'
    ],
    'excludePaths' => [
     __DIR__ . './../../aspect',
    ]
   ]);
  return $params;
 }
}

配置模块init钩子,让其启动 go-aop

<?PHP
// file: ./application/tests/tags.php
// 由于是thinkphp5.10 没有容器,所有需要在module下的tags.php文件里配置调用他

return [
 // 应用初始化
 'app_init'  => [],
 // 应用开始
 'app_begin' => [],
 // 模块初始化
 'module_init' => [
  'app\\tests\\behavior\\Bootstrap'
 ],
 // 操作开始执行
 'action_begin' => [],
 // 视图内容过滤
 'view_filter' => [],
 // 日志写入
 'log_write' => [],
 // 应用结束
 'app_end'  => [],
];

兼容测试

好了,访问 http://127.0.0.1/tests/test1/test1 显示:

hello

这不是预期的效果,在aspect定义了,访问该方法前,会输出方法的更多信息信息。
像如下内容才是预期

Calling Before Interceptor for method: app\tests\controller\Test1->aspectAction() with arguments: []

上他官方Doc看看,是一些更高级的用法。没有讲go-aop的运行机制。
上git上也没看到类似issue,额,发现作者经常在issue里回复:试一试demo。也许我该试试demo。

Run Demos

我采用的是LNMP技术栈。

  1. 假设这里有台Ubuntu你已经配置好了LNMP环境
  2. 下载代码
  3. 配置nginx
# file: /usr/share/etc/nginx/conf.d/go-aop-test.conf
server {
 listen 8008;
# listen 443 ssl;
 server_name 0.0.0.0;
 root "/usr/share/nginx/html/app/vendor/lisachenko/go-aop-php/demos";
 index index.html index.htm index.php;
 charset utf-8;

 access_log /var/log/nginx/go-aop-access.log;
 error_log /var/log/nginx/go-aop-error.log notice;

 sendfile off;
 client_max_body_size 100m;

 location ~ \.php(.*)$ {
  include       fastcgi_params;
  fastcgi_pass      127.0.0.1:9000;
  fastcgi_index      index.php;

  fastcgi_param      PATH_INFO  $fastcgi_path_info;
#  fastcgi_param     SCRIPT_FILENAME /var/www/html/app/vendor/lisachenko/go-aop-php/demos$fastcgi_script_name; #docker的配置
  fastcgi_param      SCRIPT_FILENAME /usr/share/nginx/html/api/vendor/lisachenko/go-aop-php/demos$fastcgi_script_name;
  fastcgi_param      PATH_TRANSLATED $document_root$fastcgi_path_info;
  fastcgi_split_path_info   ((?U).+\.php)(/?.+)$;
 }
}

接下来要调整下代码

  1. 访问 http://127.0.0.1:8008 试试,(估计大家都遇到了这个)

  1. 这个报错信息提示找不到这个类。来到报错的文件里。这文件使用了use找不到类,就是autoload出问题了,看到 vendor/lisachenko/go-aop-php/demos/autoload.php 这个文件。
<?PHP
···
if (file_exists(__DIR__ . '/../vendor/autoload.php')) {
 /** @var Composer\Autoload\ClassLoader $loader */
 $loader = include __DIR__ . '/../vendor/autoload.php';
 $loader->add('Demo', __DIR__);
}

可以看到这个代码第一行没找到vendor下的autoload。我们做如下调整

<?PHP
$re = __DIR__ . '/../../../vendor/autoload.php';
if (file_exists(__DIR__ . '/../../../autoload.php')) {
 /** @var Composer\Autoload\ClassLoader $loader */
 $loader = include __DIR__ . '/../../../autoload.php';
 $loader->add('Demo', __DIR__);
}

再试试,demo运行起来了。

尝试了下,运行成功

通过以上的输出,可以得出demo里是对方法运行前成功捕获。为什么在thinkphp的controller里运行就不成功呢。我决定采用断点进行调试。

通过断点我发现了这个文件

<?PHP
// file: ./vendor/lisachenko/go-aop-php/src/Instrument/ClassLoading/AopComposerLoader.php

public function loadClass($class)
{
 if ($file = $this->original->findFile($class)) {
  $isInternal = false;
  foreach ($this->internalNamespaces as $ns) {
   if (strpos($class, $ns) === 0) {
    $isInternal = true;
    break;
   }
  }

  include ($isInternal ? $file : FilterInjectorTransformer::rewrite($file));
 }
}

这是一个autoload,每个类的载入都会经过它,并且会对其判断是否为内部类,不是的都会进入后续的操作。通过断点进入 FilterInjectorTransformer,发现会对load的文件进行语法解析,并根据注册的annotation对相关的类生成proxy类。说道这,大家就明白了go-aop是如何做到切入你的程序了吧,生成的proxy类,可以在你配置的cache-dir(我配置的是./runtime/aop_cache/)里看到。

同时./runtime/aop_cache/ 文件夹下也生成了很多东西,通过查看aop_cache文件内产生了与Test1文件名相同的文件,打开文件,发现它代理了原有的Test1控制器。这一系列信息,可以得出,Go!AOP 通过"劫持" composer autoload 让每个类都进过它,根据aspect的定义来决定是否为其创建一个代理类,并植入advice。
额,ThinkPHP5是把composer autoload里的东西copy出来,放到自己autoload里,然后就没composer啥事了。然后go-aop一直等不到composer autoload下发的命令,自然就不能起作用了,so,下一步

改进ThinkPHP5

在ThinkPHP5里,默认有且只会注册一个TP5内部的 Loader,并不会把include请求下发给composer的autoload。所以,为其让go-aop起作用,那么必须让让include class的请求经过 AopComposerLoad.
我们看看这个文件

<?PHP
// ./vendor/lisachenko/go-aop-php/src/Instrument/ClassLoading/AopComposerLoader.php:57

public static function init()
{
 $loaders = spl_autoload_functions();

 foreach ($loaders as &$loader) {
  $loaderToUnregister = $loader;
  if (is_array($loader) && ($loader[0] instanceof ClassLoader)) {
   $originalLoader = $loader[0];

   // Configure library loader for doctrine annotation loader
   AnnotationRegistry::registerLoader(function ($class) use ($originalLoader) {
    $originalLoader->loadClass($class);

    return class_exists($class, false);
   });
   $loader[0] = new AopComposerLoader($loader[0]);
  }
  spl_autoload_unregister($loaderToUnregister);
 }
 unset($loader);

 foreach ($loaders as $loader) {
  spl_autoload_register($loader);
 }
}

这个文件里有个类型检测,检测autoload callback是否为Classloader类型,然而ThinkPHP5不是,通过断点你会发现ThinkPHP5是一个字符串数组,so,这里也就无法把go-aop注册到class loader的callback当中了。

这里就要提一下PHP autoload机制了,这是现代PHP非常重要的一个功能,它让我们在用到一个类时,通过名字能自动加载文件。我们通过定义一定的类名规则与文件结构目录,再加上能实现以上规则的函数就能实现自动加载了。在通过 spl_autoload_register 函数的第三个参数 prepend 设置为true,就能让其排在在TP5的loader前面,先一步被调用。

依照如上原理,就可以做如下改进
这个是为go-aop包装的新autoload,本质上是在原来的ThinkPHP5的loader上加了一个壳而已。

<?PHP
// file: ./application/tests 

namespace app\tests;

require_once __DIR__ . './../../vendor/composer/ClassLoader.php';

use think\Loader;
use \Composer\Autoload\ClassLoader;
use Go\Instrument\Transformer\FilterInjectorTransformer;
use Go\Instrument\ClassLoading\AopComposerLoader;
use Doctrine\Common\Annotations\AnnotationRegistry;

class ThinkPhpLoaderWrapper extends ClassLoader
{
 static protected $thinkLoader = Loader::class;

 /**
  * Autoload a class by it's name
  */
 public function loadClass($class)
 {
  return Loader::autoload($class);
 }

 /**
  * {@inheritDoc}
  */
 public function findFile($class)
 {
  $allowedNamespace = [
   'app\tests\controller'
  ];
  $isAllowed = false;
  foreach ($allowedNamespace as $ns) {
   if (strpos($class, $ns) === 0) {
    $isAllowed = true;
    break;
   }
  }
  // 不允许被AOP的类,则不进入AopComposer
  if(!$isAllowed)
   return false;

  $obj = new Loader;
  $observer = new \ReflectionClass(Loader::class);

  $method = $observer->getMethod('findFile');
  $method->setAccessible(true);
  $file = $method->invoke($obj, $class);
  return $file;
 }
}
<?PHP
// file: ./application/tests/behavior/Bootstrap.php 在刚刚我们新添加的文件当中
// 这个方法 \app\tests\behavior\Bootstrap::moduleInit 的后面追加如下内容

// 组成AOPComposerAutoLoader
$originalLoader = $thinkLoader = new ThinkPhpLoaderWrapper();
AnnotationRegistry::registerLoader(function ($class) use ($originalLoader) {
 $originalLoader->loadClass($class);

 return class_exists($class, false);
});
$aopLoader = new AopComposerLoader($thinkLoader);
spl_autoload_register([$aopLoader, 'loadClass'], false, true);

return $params;

在这里我们做了一个autload 并直接把它插入到了最前面(如果项目内还有其他autloader,请注意他们的先后顺序)。

最后

现在我们再访问一下 http://127.0.0.1/tests/test1/test1 你就能看到来自 aspect 输出的信息了。

最后我们做个总结:

  1. PHP7 目前没有拓展实现的 AOP
  2. ThinkPHP5 有着自己的 Autoloader
  3. Go!AOP 的AOP实现依赖Class Autoloadcallback,通过替换原文件指向Proxy类实现。
  4. ThinkPHP5 整合 Go!AOP 需要调整 autoload

更多关于thinkPHP相关内容感兴趣的读者可查看本站专题:《ThinkPHP入门教程》、《thinkPHP模板操作技巧总结》、《ThinkPHP常用方法总结》、《codeigniter入门教程》、《CI(CodeIgniter)框架进阶教程》、《Zend FrameWork框架入门教程》及《PHP模板技术总结》。

希望本文所述对大家基于ThinkPHP框架的PHP程序设计有所帮助。

(0)

相关推荐

  • 在云虚拟主机部署thinkphp5项目的步骤详解

    前言 Thinkphp作为开源的PHP框架发展到今天,无疑是国内最适合PHP学员学习和使用的框架,除了简单容易上手,更大的优点是开发文档完善.插件丰富.本文将给大家详细介绍关于在在云虚拟主机部署thinkphp5项目的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧. 查阅ThinkPHP5.0完全开发手册->部署->虚拟主机环境,了解情況后,开始修改. 环境 thinkphp 5.0.11 阿里云云虚拟系统CentOS 第一步 修改移动入口文件位置,把它移动到根目录下

  • thinkPHP5实现的查询数据库并返回json数据实例

    本文实例讲述了thinkPHP5实现的查询数据库并返回json数据.分享给大家供大家参考,具体如下: TP5 实现查询数据库返回json数据(返回json数据函数实例) 返回结果: 复制代码 代码如下: {"code":0,"msg":"\u6570\u636e\u8fd4\u56de\u6210\u529f","count":1000,"data":[{"id":617,"t

  • TP5(thinkPHP5)框架基于ajax与后台数据交互操作简单示例

    本文实例讲述了TP5(thinkPHP5)框架基于ajax与后台数据交互操作.分享给大家供大家参考,具体如下: Ajax最大的一点是页面无刷新,在页面内与服务器通信,给用户的体验非常好.可以把以前一些服务器负担的工作转嫁到客户端,利用客户端闲置的能力来处理,减轻服务器和带宽的负担,节约空间和宽带租用成本.并且减轻服务器的负担,ajax的原则是"按需取数据",可以最大程度的减少冗余请求,和响应对服务器造成的负担. 最近在用tp5开发企业微应用,很有收获,觉得最需要注意的是thinkphp

  • thinkphp5 加载静态资源路径与常量的方法

    1.加载静态资源路径 大于5.0.4版本可以直接使用 __ROOT__ :项目目录 __STATIC__ :项目目录下的static目录 __JS__ :项目目录下的static/js目录 __CSS__:项目目录下的static/css目录 我们可以使用view模板打印这些常量的具体路径 <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Insert ti

  • tp5(thinkPHP5框架)时间查询操作实例分析

    本文实例讲述了tp5(thinkPHP5框架)时间查询操作.分享给大家供大家参考,具体如下: 在项目中 可能会遇到 跨月份进行查询 比如在 当输入201809 会获取当月的开始时间$start_month 和 结束时间 $end_month 会查询2018年9月份的数据 但是当其中的一个数据是在201809到201810 ,数据库的字段是 start_time end_time 这时候 Db::name("表名")->where('start_time','<= time'

  • tp5(thinkPHP5)框架数据库Db增删改查常见操作总结

    本文实例讲述了tp5(thinkPHP5)框架数据库Db增删改查常见操作.分享给大家供大家参考,具体如下: 添加数据insert $data = [ 'name_cn' => '张三', 'name_en' => 'jack', ]; $res = Db::name('style')->insert($data); 添加数据. INSERT INTO `tf_style` (`name_cn` , `name_en`) VALUES ('张三' , 'jack') tp5还可以通过ins

  • tp5(thinkPHP5框架)captcha验证码配置及验证操作示例

    本文实例讲述了tp5(thinkPHP5框架)captcha验证码配置及验证操作.分享给大家供大家参考,具体如下: 1-安装 composer require topthink/think-captcha 1.* -vvv 2-配置文件 'captcha' => [ // 验证码字符集合3. 'codeSet' => '2345678abcdefhijkmnpqrstuvwxyzABCDEFGHJKLMNPQRTUVWXY', // 验证码字体大小(px)5. 'fontSize' =>

  • thinkPHP5实现数据库添加内容的方法

    本文实例讲述了thinkPHP5实现数据库添加内容的方法.分享给大家供大家参考,具体如下: 面对一个新框架安装好了一般不知道要干啥.那就先做一个写入功能先. 先做好准备工作先,首先要连接上数据库. 配置文件在application下的database.php里 return [ // 数据库类型 'type' => 'mysql', // 服务器地址 'hostname' => '127.0.0.1', // 数据库名 'database' => 'shoptest', // 用户名 '

  • tp5(thinkPHP5)操作mongoDB数据库的方法

    本文实例讲述了tp5(thinkPHP5)操作mongoDB数据库的方法.分享给大家供大家参考,具体如下: 1.通过composer安装 composer require mongodb/mongodb 2.使用 <?php /** * @author: jim * @date: 2017/11/17 */ namespace app\index\controller; use think\Controller; use MongoDB\Driver\Manager; use MongoDB\C

  • ThinkPHP5 框架引入 Go AOP,PHP AOP编程项目详解

    本文实例讲述了ThinkPHP5 框架引入 Go AOP,PHP AOP编程.分享给大家供大家参考,具体如下: 项目背景 目前开发的WEB软件里有这一个功能,PHP访问API操作数据仓库,刚开始数据仓库小,没发现问题,随着数据越来越多,调用API时常超时(60s).于是决定采用异步请求,改为60s能返回数据则返回,不能则返回一个异步ID,然后轮询是否完成统计任务.由于项目紧,人手不足,必须以最小的代价解决当前问题. 方案选择 重新分析需求,并改进代码 采用AOP方式改动程序 从新做需求分析,以及

  • Spring框架实现AOP的两种方式详解

    目录 第一种AOP实现方式 AfterLog Log 配置文件 实例调用 定义接口 第二种AOP实现方式 第一种AOP实现方式 AfterLog package com.xxx.demo.service1; import org.junit.After; import org.springframework.aop.AfterReturningAdvice; import java.lang.reflect.Method; public class AfterLog implements Aft

  • Spring IOC和aop的原理及实例详解

    这篇文章主要介绍了Spring IOC和aop的原理及实例详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架.特点是面向接口编程,松耦合. 1:IOC(控制反转) 别名(DI:依赖注入) 首先来一段ioc的实现原来代码: public class ClassPathXmlApplicationContext implements BeanFactory { privat

  • Spring AOP执行先后顺序实例详解

    这篇文章主要介绍了Spring AOP执行先后顺序实例详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 众所周知,spring声明式事务是基于AOP实现的,那么,如果我们在同一个方法自定义多个AOP,我们如何指定他们的执行顺序呢? 网上很多答案都是指定order,order越小越是最先执行,这种也不能算是错,但有些片面. 配置AOP执行顺序的三种方式: 通过实现org.springframework.core.Ordered接口 @Compo

  • SpringBoot利用AOP实现一个日志管理详解

    目录 1. 需求 2. 新建一张日志表 3. 写相应的Controller层 4.Service接口层 5.Service实现 6.Mapper接口 7.Mapper.xml(我用的是Mybatis) 8.CspLog 9.实体类SysOperCspLog 10. 定义日志管理的切面 11.AsyncFactoryCsp 12. 写一个Controller的Demo来执行一条日志试试 1. 需求 目前有这么个问题,有两个系统CSP和OMS,这俩系统共用的是同一套日志操作:Log;目前想区分下这俩

  • Android AOP之注解处理解释器详解(二)

    Android APO 注解处理解释器 相关文章: Android AOP注解Annotation详解(一) Android AOP之注解处理解释器详解(二) Android AOP 注解详解及简单使用实例(三) 一.提取Annotation信息 当开发者使用了Annotation修饰了类.方法.Field等成员之后,这些Annotation不会自己生效,必须由开发者提供相应的代码来提取并处理Annotation信息.这些处理提取和处理Annotation的代码统称为APT(Annotation

  • Spring AOP事务管理的示例详解

    目录 转账案例-环境搭建 步骤1:准备数据库表 步骤2:创建项目导入jar包 步骤3:根据表创建模型类 步骤4:创建Dao接口 步骤5:创建Service接口和实现类 步骤6:添加jdbc.properties文件 步骤7:创建JdbcConfig配置类 步骤8:创建MybatisConfig配置类 步骤9:创建SpringConfig配置类 步骤10:编写测试类 事务管理 转账案例-环境搭建 步骤1:准备数据库表 之前我们在整合Mybatis的时候已经创建了这个表,可以直接使用 create

  • Java并发编程之详解ConcurrentHashMap类

    前言 由于Java程序员常用的HashMap的操作方法不是同步的,所以在多线程环境下会导致存取操作数据不一致的问题,Map接口的另一个实现类Hashtable 虽然是线程安全的,但是在多线程下执行效率很低.为了解决这个问题,在java 1.5版本中引入了线程安全的集合类ConcurrentMap. java.util.concurrent.ConcurrentMap接口是Java集合类框架提供的线程安全的map,这意味着多线程同时访问它,不会影响map中每一条数据的一致性.ConcurrentM

  • java多线程编程技术详解和实例代码

     java多线程编程技术详解和实例代码 1.   Java和他的API都可以使用并发. 可以指定程序包含不同的执行线程,每个线程都具有自己的方法调用堆栈和程序计数器,使得线程在与其他线程并发地执行能够共享程序范围内的资源,比如共享内存,这种能力被称为多线程编程(multithreading),在核心的C和C++语言中并不具备这种能力,尽管他们影响了JAVA的设计. 2.   线程的生命周期 新线程的生命周期从"新生"状态开始.程序启动线程前,线程一直是"新生"状态:

  • nodejs中的异步编程知识点详解

    简介 因为javascript默认情况下是单线程的,这意味着代码不能创建新的线程来并行执行.但是对于最开始在浏览器中运行的javascript来说,单线程的同步执行环境显然无法满足页面点击,鼠标移动这些响应用户的功能.于是浏览器实现了一组API,可以让javascript以回调的方式来异步响应页面的请求事件. 更进一步,nodejs引入了非阻塞的 I/O ,从而将异步的概念扩展到了文件访问.网络调用等. 今天,我们将会深入的探讨一下各种异步编程的优缺点和发展趋势. 同步异步和阻塞非阻塞 在讨论n

随机推荐