million

million 查看完整档案

填写现居城市  |  填写毕业院校  |  填写所在公司/组织填写个人主网站
编辑
_ | |__ _ _ __ _ | '_ \| | | |/ _` | | |_) | |_| | (_| | |_.__/ \__,_|\__, | |___/ 该用户太懒什么也没留下

个人动态

million 发布了文章 · 9月10日

基于SSM框架快速搭建maven后台项目

基于SSM框架快速搭建maven后台项目

环境准备

1.IntelliJ IDEA

2.Maven

https://maven.apache.org/

3.MYSQL

https://www.mysql.com/

4.Tomcat

http://tomcat.apache.org/

5.JDk

https://www.oracle.com/java/t...

SSM框架简介

无论你是前端,APP,后端开发工程师,搭建一个自己的后台对于学习都是很有帮助的,本文是作者搭建SSM后端环境的记录,阅读完本文你也可以快速搭建一个属于自己的后端代码。

SSM框架是Spring MVC ,Spring和Mybatis框架的整合,是标准的MVC模式,将整个系统划分为View层,Controller层,Service层,DAO层四层,使用Spring MVC负责请求的转发和视图管理,Spring实现业务对象管理,Mybatis作为数据对象的持久化引擎。

数据库配置

首先我们创建一个数据库和表。

创建数据库的SQL

CREATE DATABASE time_manager_app

创建数据表的SQL

DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
  `id` int(8) NOT NULL COMMENT '主键',
  `name` varchar(255) COLLATE utf8_bin NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;

向数据表中插入一条数据

INSERT INTO `user` VALUES ('1', '1');

创建工程

现在我们打开IDEA创建我们的maven工程

File->New->Project

选择maven,选中红框中的选项,点击Next

红框中的两个选项随便输入,点击Next

红框中选择本地安装的maven软件的位置,点击Next

点击finish

现在我们的maven工程就创建好了

接下来我们要创建一些工程需要的文件夹

右键选择SRC->NEW->Directory

创建好的文件目录如下

右键JAVA将它设置为Source Root

右键resource将它设置为Resources Root

工程创建完毕,接下来我们编写我们需要的配置文件

配置文件编写

首先我们在POM.XML中添加我们需要的依赖包

注意,复制时从备注里的copy开始,复制到copy结束,不要整个文件复制

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.ssmtest</groupId>
    <artifactId>ssmtest</artifactId>
    <version>1.0-SNAPSHOT</version>
      <!--从这里开始copy -->
    <!-- 用来设置版本号 -->
    <properties>
        <spring.version>4.2.4.RELEASE</spring.version>
        <mybatis.version>3.2.8</mybatis.version>
        <slf4j.version>1.7.12</slf4j.version>
        <log4j.version>1.2.17</log4j.version>
        <druid.version>1.0.9</druid.version>
    </properties>
  
    <!-- 用到的jar包 -->
    <dependencies>
        <!-- 单元测试 -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.11</version>
            <!-- 表示开发的时候引入,发布的时候不会加载此包 -->
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.6.8</version>
        </dependency>
        <!-- spring框架包 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-oxm</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-expression</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-orm</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <!-- spring框架包 -->
        <!-- mybatis框架包 -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>${mybatis.version}</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>1.2.2</version>
        </dependency>
        <!-- mybatis框架包 -->
        <!-- 数据库驱动 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.35</version>
        </dependency>
        <!-- 导入dbcp的jar包,用来在applicationContext.xml中配置数据库 -->
        <dependency>
            <groupId>commons-dbcp</groupId>
            <artifactId>commons-dbcp</artifactId>
            <version>1.4</version>
        </dependency>
        <!-- jstl标签类 -->
        <dependency>
            <groupId>jstl</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
        </dependency>
        <!-- log -->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>${log4j.version}</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>${slf4j.version}</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>${slf4j.version}</version>
        </dependency>
        <!-- 连接池 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>${druid.version}</version>
        </dependency>

        <!--加入对json转换-->
        <!-- JSON: jackson -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-annotations</artifactId>
            <version>2.5.0</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
            <version>2.5.0</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.5.0</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.jr</groupId>
            <artifactId>jackson-jr-all</artifactId>
            <version>2.5.0</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.46</version>
        </dependency>
        <dependency>
            <groupId>c3p0</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.1.2</version>
            <type>jar</type>
            <scope>compile</scope>
        </dependency>
    </dependencies>

    <build>
        <finalName>jackwu</finalName>
        <!-- java编译插件,如果maven的设置里配置好jdk版本就不用 -->
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.2</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
        </plugins>

        <!--打包配置文件-->
        <resources>
            <resource>
                <directory>src\main\resources</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                    <include>**/*.tld</include>
                </includes>
                <filtering>false</filtering>
            </resource>
            <resource>
                <directory>src\main\java</directory>
                <excludes>
                    <exclude>**/*.java</exclude>
                </excludes>
            </resource>
        </resources>
    </build>
    <!--copy在这里结束 -->

</project>

PS:如果你的网络环境不好,Maven下载依赖包需要一段时间,不要着急,继续接下来的配置就可以了

接下来我们在WEB-INF目录下创建web.xml

web.xml内容如下,整个copy就可以

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">

    <!-- 配置加载类路径的配置文件 -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath*:applicationContext.xml</param-value>
    </context-param>

    <!-- 配置监听器 -->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <listener>
        <listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
    </listener>

    <!-- 解决中文乱码过滤器 -->
    <filter>
        <filter-name>characterEncodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>characterEncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>


    <!-- 前端控制器(加载classpath:springmvc.xml 服务器启动创建servlet) -->
    <servlet>
        <servlet-name>dispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!-- 配置初始化参数,创建完DispatcherServlet对象,加载springmvc.xml配置文件 -->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:spring-mvc.xml</param-value>
        </init-param>
        <!-- 服务器启动的时候,让DispatcherServlet对象创建 -->
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>dispatcherServlet</servlet-name>
        <url-pattern>*.do</url-pattern>
    </servlet-mapping>



    <welcome-file-list>
        <welcome-file>index.html</welcome-file>
        <welcome-file>index.htm</welcome-file>
        <welcome-file>index.jsp</welcome-file>
        <welcome-file>default.html</welcome-file>
        <welcome-file>default.htm</welcome-file>
        <welcome-file>default.jsp</welcome-file>
    </welcome-file-list>
</web-app>

接着我们在resources文件夹下创建applicationContext.xml

同样内容整个复制

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop.xsd
    http://www.springframework.org/schema/tx
    http://www.springframework.org/schema/tx/spring-tx.xsd">

    <!-- 开启注解扫描,管理service和dao -->
    <context:component-scan base-package="service">
    </context:component-scan>
    <context:component-scan base-package="dao">
    </context:component-scan>

    <context:property-placeholder location="classpath:db.properties"/>
    <!-- 配置连接池 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="${jdbc.driver}"/>
        <property name="jdbcUrl" value="${jdbc.url}"/>
        <property name="user" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>
    <!-- 把交给IOC管理 SqlSessionFactory -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>

    </bean>
    <!-- 扫描dao接口 -->
    <bean id="mapperScanner" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="dao"/>
    </bean>

    <!-- 配置Spring的声明式事务管理 -->
    <!-- 配置事务管理器 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <tx:annotation-driven transaction-manager="transactionManager"/>

</beans>

接着我们在resource文件夹下创建spring-mvc.xml

同样内容整个复制

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="
           http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/mvc
           http://www.springframework.org/schema/mvc/spring-mvc.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context.xsd
           http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd
           ">

    <!-- 扫描controller的注解,别的不扫描 -->
    <context:component-scan base-package="controller">
    </context:component-scan>

    <!-- 配置视图解析器 -->
    <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!-- JSP文件所在的目录 -->
        <property name="prefix" value="/pages/" />
        <!-- 文件的后缀名 -->
        <property name="suffix" value=".jsp" />
    </bean>


    <!-- 开启对SpringMVC注解的支持 -->
    <mvc:annotation-driven />

    <!--
        支持AOP的注解支持,AOP底层使用代理技术
        JDK动态代理,要求必须有接口
        cglib代理,生成子类对象,proxy-target-class="true" 默认使用cglib的方式
    -->

    <!--
        支持AOP的注解支持,AOP底层使用代理技术
        JDK动态代理,要求必须有接口
        cglib代理,生成子类对象,proxy-target-class="true" 默认使用cglib的方式
    -->
    <aop:aspectj-autoproxy proxy-target-class="true"/>
    <!-- 输出对象转JSON支持 -->
    <bean
            class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
        <property name="messageConverters">
            <list>
                <bean
                        class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
                    <property name="supportedMediaTypes">
                        <list>
                            <value>text/html;charset=UTF-8</value>
                            <value>text/plain;charset=UTF-8</value>
                            <value>application/json;charset=UTF-8</value>
                        </list>
                    </property>
                </bean>
            </list>
        </property>
    </bean>

</beans>

接着我们在resource文件夹下创建db.properties

同样内容整个复制

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/time_manager_app?characterEncoding=utf-8
jdbc.username=root
jdbc.password=root

现在我们的工程文件结构是这样的

接下来我们要编写我们的java代码

Java代码编写

首先我们创建几个需要的文件夹

接着我们在相应的文件夹下创建我们的java文件

文件结构如下

每个文件下的代码如下

UserController

package controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import service.UserService;

@Controller
@RequestMapping("/user")
public class UserController {
    @Autowired
    private UserService userService;

    @RequestMapping("/find.do")
    @ResponseBody
    public Object findAll(){
        Object a = userService.findAll();
        System.out.println(a);
        return  a;
    }
}

UserDao

package dao;

import org.apache.ibatis.annotations.Select;
import pojo.User;

import java.util.List;

public interface UserDao {
    @Select("select * from user")
    public List<User> findAll();
}

User

package pojo;

public class User {
    private int id;
    private String name;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

UserServiceImpl

package service.impl;

import dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import pojo.User;
import service.UserService;

import java.util.List;

@Service
@Transactional
public class UserServiceImpl implements UserService {
    @Autowired
    private UserDao userDao;
    public List<User> findAll(){
        return  userDao.findAll();
    }
}

UserService

package service;

import pojo.User;

import java.util.List;

public interface UserService {

    public List<User> findAll();
}

到这里代码应该是没有错误的.如果有错误,可能是依赖包没有下载好,等待一下

接下来我们配置tomcat

配置tomcat

点击idea右上角的位置,接着点击edit

按顺序点击红色框的位置

在红框处选择tomcat版本

接着配置我们的war包,点击红色框处

选择第二个

清空application context中的内容,点击apply

NO NO NO配置中间我们出现了一个问题

应该把resource文件夹更名为resources Sorry

正确的工程路径如下

启动程序

接下来我们要启动我们的程序,测试是否配置成功

按顺序点击红框处

等待打包成功,会出现target文件夹,控制台log如下

接着点击绿色箭头,启动程序

测试

打开我们的浏览器,输入http://localhost:8080/user/find.do

Amazing数据库中的一条数据已经成功返回了,到此我们的SSM工程配置成功。

代码仓库位置

如果你觉得配置太麻烦,你也可以在github上下载作者配置好的项目

项目地址https://github.com/jack0-0wu/...

后续还会创作几篇如何快速使用此项目完善后台逻辑,欢迎关注

欢迎关注微信公众号,文章会首发在微信公众号。
可以搜索公众号名 ‘’杰克的程序人生‘’

师者,所以传道授业解惑也,教师节快乐

查看原文

赞 0 收藏 0 评论 0

million 发布了文章 · 9月5日

操作系统——计算机硬件简介

计算机硬件简介

从概念上讲,一台计算机可以抽象为下图的模型


图0.1计算机抽象模型
CPU,内存以及I/O设备都由一条系统总线连接起来并通过总线与其他设备通信

CPU

CPU是计算机的大脑,它从内存中取出指令并执行。

CPU的工作流程

从内存中取出指令,对取出的指令进行解码,执行,CPU就是一直不断的重复这个过程。

wVb1Et.png
​ 图1.1CPU工作流程

超标量CPU

流水线作业效率不高,于是就引入了超标量CPU

超标量CPU是这样工作的,多个取值和解码同时进行,取值解码完成后的指令会进入缓冲区,缓冲区对应多个执行单元,每当缓冲区中有指令且有空闲的执行单元时,就会从缓冲区取出指令进入执行单元执行.

​ 图1.2超标量CPU工作流程

内核态和用户态

多数CPU多有两种模式,内核态和用户态

在内核态运行时,CPU可以执行指令集的每一条指令,使用硬件的全部功能

在用户态运行时,CPU只能执行指令集的一个子集和访问所有功能的一个自己

在台式机和服务器上,操作系统在内核态运行。在大多数嵌入式系统中,一部分操作系统在内核态运行,其余部分在用户态运行

存储器

存储器分为四个层次

寄存器 高速缓存 内存 硬盘

​ 图2.1存储器的四层

寄存器

寄存器存在于CPU中,访问和CPU一样快,没有时延

高速缓存

常用的高速缓存行放置在CPU内部或非常接近CPU的位置

高速缓存命中

当程序需要读取一个字时,检查所需要的字是否在高速缓存中,如果在高速缓存中,称为高速缓存命中

如果高速缓存未命中,就要通过总线,把访问请求传递到内存,这带来了访问速度的下降

PS:缓存

大量的资源存在于计算机存储器的某一处,其中一小部分资源会被频繁的用到,把频繁用的资源放到比大量资源更高层次的存储器中,这就是缓存。

计算机在读取文件时,将硬盘中频繁用的文件放入内存中,这就是缓存的应用。

使用缓存时我们需要考虑几个问题

1)何时把资源放入缓存中

2)把资源放在存储器的哪一层上

3)在缓存满了时,把什么内容从缓存中移走

4)移走的内容又该放到何处

内存

内存通常成为随机访问存储器(RAM),速度比磁盘快,程序先进入这里执行,内存具有断电数据消失的特性。

PS:闪存

速度介于内存和磁盘间,断电后数据不消失

磁盘

磁盘工作流程

像是老式的唱片机,一个又一个重叠起的圆盘,每个圆盘上配有一个指针,当从磁盘读取数据时指针不断旋转,读取一段环形区域,这段环形区域叫做磁道。
wVxqYR.png

​ 图2.2 磁盘的工作流程

我们常讲硬盘的转速,多少MB每秒,从磁盘的工作流程上我们可以了解到,磁盘的转速就是磁盘臂旋转的速度

固态硬盘

固态硬盘和普通磁盘不是同一种工作方式,固态硬盘其实是一种闪存

虚拟内存

计算机的虚拟内存机制就是将磁盘中需要反复读取的内容放到内存中,加快计算机的速度,也是一种缓存的应用

I/O设备

I/O设备分为两个部分 设备控制器和设备本身

设备控制器

设备控制器是插在电路板上的一块芯片或一组芯片,他是操作系统和设备之间的桥梁,配合操作系统操作设备,操作系统对它发成命令,它对操作系统的命令进行复杂的转换,控制设备。

​ 图3.1设备控制器

设备本身

硬盘,键盘,鼠标,显示器等等

设备本身有一个相对简单的标准化接口,比如创建的SATA硬盘,SATA就是设备的接口名

设备驱动程序

操作系统如何操作设备控制器呢,这个答案就是在操作系统上安装设备控制程序,设备控制程序负责与控制器对话,发出命令,接受响应。

实现输入输出的三种方式

1)忙等待:用户程序发起一个系统调用,内核将其翻译成一个对应设备驱动程序的过程调用。设备驱动程序启动I/O,循环的检查设备是否完成工作,当完成工作后,设备驱动程序将设备返回的数据送到指定的位置,随后操作系统将控制返回给调用者。

缺点:显而易见,在设备执行完成前,要一直占用CPU,CPU要一直循环的检查,这期间用户什么也不能干

2)中断控制:设备驱动程序启动设备,让该设备在操作完成时发出一个中断,驱动程序将中断传递给操作系统。

3)直接存储器访问:为I/O操作使用一种特殊的芯片(DMA),它无需持续的CPU干预,直接控制内存和控制器之间通信,操作完成后,也发出中断。

总线

一条连接计算机上所有硬件的线

PCI总线

多个设备使用同一条导线传输数据,当多个设备需要发送数据时,仲裁器决定哪个设备可以使用总线。(已经过时了)

PCIe总线

端到端的链路,即每个设备都拥有单独的一条导线用来发送数据

启动计算机

学完了计算机相关的硬件知识,我们来了解一下启动计算机时,计算机内部是如何工作的

首先,BIOS开始运行,它扫描I/O设备,存储器是否安装正常,接着扫描出总线上的所有设备,随后从硬盘中找出一个启动装载模块,这个启动装载模块负责启动操作系统(WINDOWS/LINUX),然后,操作系统询问BIOS获得每种设备的配置信息,操作系统检查对应设备的驱动程序是否安装正常,如果全部正常,操作系统将他们调入内核,创建需要的所有进程,在终端上启动登录程序。

欢迎关注微信公众号,文章会首发在微信公众号。
可以搜索公众号名 ‘’杰克的程序人生‘’

学习,源于未知的热爱

查看原文

赞 0 收藏 0 评论 0

million 发布了文章 · 8月31日

Flutter开发基础——Dart语法规范

字符串

两个常量字符串(不是变量,是放在引号中的字符串),你不需要使用 + 来连接它们。

推荐的写法

print(
    'ERROR: Parts of the spaceship are on fire. Other '
    'parts are overrun by martians. Unclear which are which.');

不推荐的写法

print('ERROR: Parts of the spaceship are on fire. Other ' +
    'parts are overrun by martians. Unclear which are which.');
不要在字符串中使用不必要的大括号

如果要插入是一个简单的标识符,并且后面没有紧跟着的字母,则应省略 {}

推荐的写法

'Hi, $name!'
"Wear your wildest $decade's outfit."
//标识符后面有紧跟着的字母了 加上大括号用以区分
'Wear your wildest ${decade}s outfit.'

不推荐的写法

'Hi, ${name}!'
"Wear your wildest ${decade}'s outfit."

布尔值

使用? ?将空值转换为布尔值。

当表达式的值可以为真、假或null,并且您需要将结果传递给不接受null的对象时,此规则适用。一个常见的情况是一个判断空值的方法调用被用作条件:

不推荐的写法

if (optionalThing?.isEnabled) {
  print("Have enabled thing.");
}

如果optionalThing为空,此代码将抛出异常。(if只支持判断bool值,不支持null)要解决这个问题,您需要将null值“转换”为true或false。虽然您可以使用==来完成此操作,但我们建议使用?? :

推荐的写法

//如果你想要optionalThing是空值时返回false
optionalThing?.isEnabled ?? false;

//如果你想要optionalThing是空值时返回true
optionalThing?.isEnabled ?? true;

不推荐的写法

// 如果你想要optionalThing是空值时返回false
optionalThing?.isEnabled == true;

// 如果你想要optionalThing是空值时返回true
optionalThing?.isEnabled != false;

PS:如果这里你有些不理解,可以先阅读一下作者之前的文章<u>Flutter基础——Dart语法</u>,学习一下'?' '??' '??=' 的使用

两种操作都会产生同样的结果,而且做的都是正确的事情,但首选??操作符,主要有三个原因。

  1. ??运算符清楚地表明,代码与空值有关。
  2. 当左边的布尔表达式不会为空时,==true看起来像是多余的。
  3. ??false 和 ??true 清楚地表明了当表达式为null时将使用什么值。对于==true,你必须清楚左边表达式的布尔逻辑,才会明白想要 左边表达式是null时返回false 为什么要用== true。

集合

Dart 集合中原生支持了四种类型:list, map, queue, 和 set。 下面是应用于集合的最佳实践。

尽可能的使用集合字面量。

两种方式来构造一个空的可变 list : []List() 。 同样,有三种方式来构造一个空的Map map:{}Map(), 和 LinkedHashMap()

如果想创建一个固定不变的 list 或者其他自定义集合类型,这种情况下你需要使用构造函数。 否则,使用字面量语法更加优雅。 核心库中暴露这些构造函数易于扩展,但是通常在 Dart 代码中并不使用构造函数。

推荐的写法

var points = [];
var addresses = {};

不推荐的写法

var points = List();
var addresses = Map();

如果需要的话,你可以提供一个泛型。

推荐的写法

var points = <Point>[];
var addresses = <String, Address>{};

不推荐的写法

var points = List<Point>();
var addresses = Map<String, Address>();

注意,对于集合类的 命名 构造函数则不适用上面的规则。 List.from()Map.fromIterable() 都有其使用场景。 如果需要一个固定长度的结合,使用 List() 来创建一个固定长度的 list 也是合理的。

不要使用 .length 来判断一个集合是否为空。

通过调用 .length 来判断集合是否包含内容是非常低效的。相反,Dart 提供了更加高效率和易用的 getter 函数:.isEmpty.isNotEmpty。 使用这些函数并不需要对结果再次取非(list.length ! =0)

推荐的写法

if (lunchBox.isEmpty) return 'so hungry...';
if (words.isNotEmpty) return words.join(' ');

不推荐的写法

if (lunchBox.length == 0) return 'so hungry...';
if (!words.isEmpty) return words.join(' ');
不要使用 List.from() 除非想修改结果的类型。

给定一个可迭代的对象,有两种常见方式来生成一个包含相同元素的 list:

var copy1 = iterable.toList();
var copy2 = List.from(iterable);

推荐的写法

明显的区别是前一个更短。 更重要的区别在于第一个保留了原始对象的类型参数:

// 创建一个 List<int>:
var iterable = [1, 2, 3];

// 输出 "List<int>":
print(iterable.toList().runtimeType);

不推荐的写法

// 创建一个 List<int>:
var iterable = [1, 2, 3];

// 输出 "List<dynamic>":
print(List.from(iterable).runtimeType);

如果你想要改变原始对象的类型参数,那么可以调用 List.from()

推荐的写法

var numbers = [1, 2.3, 4]; // List<num>.
numbers.removeAt(1); // 现在集合里只包含int型
var ints = List<int>.from(numbers);

但是如果你的目的只是复制可迭代对象并且保留元素原始类型, 或者并不在乎类型,那么请使用 toList()

参数

使用 = 来分隔参数名和参数默认值。

由于遗留原因,Dart 同时支持 := 作为参数名和默认值的分隔符。 为了与可选的位置参数保持一致,请使用 =

推荐的写法

void insert(Object item, {int at = 0}) { ... }

不推荐的写法

void insert(Object item, {int at: 0}) { ... }
不要 显式的为参数设置 null 值。

如果你创建了一个可选参数,那么就不要为其赋默认值, Dart 默认使用 null 作为默认值,所以这里不需要为其 null 赋值语句。

推荐的写法

void error([String message]) {
  stderr.write(message ?? '\n');
}

不推荐的写法

void error([String message = null]) {
  stderr.write(message ?? '\n');
}

变量

不要 显示的为参数初始化 null 值。

在Dart中,未自动显式初始化的变量或字段将初始化为 null 。 语言保证了赋值的可靠性。在 Dart 中没有“未初始化内存”的概念。 所以使用 = null 是多余的。

推荐的写法

int _nextId;

class LazyId {
  int _id;

  int get id {
    if (_nextId == null) _nextId = 0;
    if (_id == null) _id = _nextId++;

    return _id;
  }
}

不推荐的写法

int _nextId = null;

class LazyId {
  int _id = null;

  int get id {
    if (_nextId == null) _nextId = 0;
    if (_id == null) _id = _nextId++;

    return _id;
  }
}
避免 保存可计算的结果

在设计类的时候,你常常希望暴露底层状态的多个表现属性。 常常你会发现在类的构造函数中计算这些属性,然后保存起来:

不推荐的写法

class Circle {
  num radius;
  num area;
  num circumference;

  Circle(num radius)
      : radius = radius,
        area = pi * radius * radius,
        circumference = pi * 2.0 * radius;
}

上面的代码有两个不妥之处。首先,这样浪费了内存。 严格来说面积和周长是缓存数据。 他们保存的结果可以通过已知的数据计算出来。 他们减少了 CPU 消耗却增加了内存消耗。 我们还没有权衡,到底存不存在性能问题?

更坏的情况是,上面的代码是 错的 。上面的缓存是无效的— 你如何知道什么时候缓存失效了需要重新计算? 在这里,我们永远不会从新计算,即使 radius 是可变的。 你可以给 radius 设置一个不同的值,但是 areacircumference 还是之前的值。

为了正确处理缓存失效,我们需要这样做:

不推荐的写法

class Circle {
  num _radius;
  num get radius => _radius;
  set radius(num value) {
    _radius = value;
    _recalculate();
  }

  num _area;
  num get area => _area;

  num _circumference;
  num get circumference => _circumference;

  Circle(this._radius) {
    _recalculate();
  }

  void _recalculate() {
    _area = pi * _radius * _radius;
    _circumference = pi * 2.0 * _radius;
  }
}

这需要编写、维护、调试以及阅读更多的代码。 如果你一开始这样写代码:

推荐的写法

class Circle {
  num radius;

  Circle(this.radius);

  num get area => pi * radius * radius;
  num get circumference => pi * 2.0 * radius;
}

上面的代码更加简洁、使用更少的内存、减少出错的可能性。 它尽可能少的保存了表示圆所需要的数据。 这里没有字段需要同步,因为这里只有一个有效数据源。

成员

在 Dart 中,对象成员可以是函数(方法)或数据(实例变量)。 下面是关于对象成员的最佳实践。

不要 为字段创建不必要的 getter 和 setter 方法。

推荐的写法

class Box {
  var contents;
}

不推荐的写法

class Box {
  var _contents;
  get contents => _contents;
  set contents(value) {
    _contents = value;
  }
}
推荐 使用 final 关键字来创建只读属性

如果你有一个变量,对于外部代买来说只能读取不能修改, 最简单的做法就是使用 final 关键字来标记这个变量。

推荐的写法

class Box {
  final contents = [];
}

不推荐的写法

class Box {
  var _contents;
  get contents => _contents;
}
不要 使用 this. ,在重定向命名函数和避免冲突的情况下除外。

只有当局部变量和成员变量名字一样的时候,你才需要使用 this. 来访问成员变量。 只有两种情况需要使用 this. 。其中一种情况是要访问的局部变量和成员变量命名一样的时候:

推荐的写法

class Box {
  var value;

  void clear() {
    update(null);
  }

  void update(value) {
    this.value = value;
  }
}

不推荐的写法

class Box {
  var value;

  void clear() {
    this.update(null);
  }

  void update(value) {
    this.value = value;
  }
}
尽可能的在定义变量的时候初始化变量值。

如果一个字段不依赖于构造函数中的参数, 则应该在定义的时候就初始化字段值。 这样可以减少需要的代码并可以确保在有多个构造函数的时候你不会忘记初始化该字段。

不推荐的写法

class Folder {
  final String name;
  final List<Document> contents;

  Folder(this.name) : contents = [];
  Folder.temp() : name = 'temporary'; // Oops! Forgot contents.
}

推荐的写法

class Folder {
  final String name;
  final List<Document> contents = [];

  Folder(this.name);
  Folder.temp() : name = 'temporary';
}

当然,对于变量取值依赖构造函数参数的情况以及不同的构造函数取值也不一样的情况, 则不适合本条规则。

构造函数

尽可能的使用初始化形式。

许多字段直接使用构造函数参数来初始化,如:

不推荐的写法

class Point {
  num x, y;
  Point(num x, num y) {
    this.x = x;
    this.y = y;
  }
}

为了初始化一个字段,我们需要取_四_次 x 。使用下面的方式会更好:

推荐的写法

class Point {
  num x, y,z;
  Point(this.x, this.y,{this.z});
}

这里的位于构造函数参数之前的 this. 语法被称之为初始化形式(initializing formal)。 有些情况下这无法使用这种形式。特别是,这种形式下在初始化列表中无法看到变量。 但是如果能使用该方式,就应该尽量使用。(如果使用命名参数)

; 来替代空的构造函数体 {}

在 Dart 中,没有具体函数体的构造函数可以使用分号结尾。 (事实上,这是不可变构造函数的要求。)

推荐的写法

class Point {
  int x, y;
  Point(this.x, this.y);
}

不推荐的写法

class Point {
  int x, y;
  Point(this.x, this.y) {}
}
不要 使用 new

创建对象不要使用new

推荐的写法

Widget build(BuildContext context) {
  return Row(
    children: [
      RaisedButton(
        child: Text('Increment'),
      ),
      Text('Click!'),
    ],
  );
}

不推荐的写法

Widget build(BuildContext context) {
  return new Row(
    children: [
      new RaisedButton(
        child: new Text('Increment'),
      ),
      new Text('Click!'),
    ],
  );
}

异步

推荐 使用 async/await 而不是直接使用底层的特性。

显式的异步代码是非常难以阅读和调试的, 即使使用很好的抽象(比如 future)也是如此。 这就是为何 Dart 提供了 async/await。 这样可以显著的提高代码的可读性并且让你可以在异步代码中使用语言提供的所有流程控制语句。

推荐的写法

Future<int> countActivePlayers(String teamName) async {
  try {
    var team = await downloadTeam(teamName);
    if (team == null) return 0;

    var players = await team.roster;
    return players.where((player) => player.isActive).length;
  } catch (e) {
    log.error(e);
    return 0;
  }
}

不推荐写法

Future<int> countActivePlayers(String teamName) {
  return downloadTeam(teamName).then((team) {
    if (team == null) return Future.value(0);

    return team.roster.then((players) {
      return players.where((player) => player.isActive).length;
    });
  }).catchError((e) {
    log.error(e);
    return 0;
  });
}

标识符

在 Dart 中标识符有三种类型。

  • UpperCamelCase 每个单词的首字母都大写,包含第一个单词。
  • lowerCamelCase 每个单词的首字母都大写,除了第一个单词, 第一个单词首字母小写,即使是缩略词。
  • lowercase_with_underscores 只是用小写字母单词,即使是缩略词, 并且单词之间使用 _ 连接。
使用 UpperCamelCase 风格命名类型。

Classes(类名)、 enums(枚举类型)、 typedefs(类型定义)、 以及 type parameters(类型参数)应该把每个单词的首字母都大写(包含第一个单词), 不使用分隔符。

class SliderMenu { ... }

class HttpRequest { ... }

typedef Predicate = bool Function<T>(T value);
要在文件夹源文件中使用 lowercase_with_underscores 方式命名。
lowercase_with_underscores 风格命名库和源文件名。

推荐的写法

library peg_parser.source_scanner;

import 'file_system.dart';
import 'slider_menu.dart';

不推荐的写法

library pegparser.SourceScanner;

import 'file-system.dart';
import 'SliderMenu.dart';
使用 lowercase_with_underscores 风格命名导入的前缀。

推荐的写法

import 'dart:math' as math;
import 'package:angular_components/angular_components'
    as angular_components;
import 'package:js/js.dart' as js;

不推荐的写法

import 'dart:math' as Math;
import 'package:angular_components/angular_components'
    as angularComponents;
import 'package:js/js.dart' as JS;
使用 lowerCamelCase 风格来命名其他的标识符。

类成员、顶级定义、变量、参数以及命名参数等 除了第一个单词,每个单词首字母都应大写,并且不使用分隔符。

推荐的写法

var item;

HttpRequest httpRequest;

void align(bool clearItems) {
  // ...
}
把超过两个字母的首字母大写缩略词和缩写词当做普通单词。

首字母大写缩略词比较难阅读, 特别是多个缩略词连载一起的时候会引起歧义。 例如,一个以 HTTPSFTP 开头的名字, 没有办法判断它是指 HTTPS FTP 还是 HTTP SFTP 。

为了避免上面的情况,缩略词和缩写词要像普通单词一样首字母大写, 两个字母的单词除外。 (像 ID 和 Mr. 这样的双字母缩写词仍然像一般单词一样首字母大写。)

推荐的写法

HttpConnectionInfo
uiHandler
IOStream
HttpRequest
Id
DB

不推荐的写法

HTTPConnection
UiHandler
IoStream
HTTPRequest
ID
Db

译者注:

  • acronyms :首字母缩略词,指取若干单词首字母组成一个新单词,如:HTTP = HyperText Transfer Protocol
  • abbreviations : 缩写词,指取某一单词的部分字母(或其他缩短单词的方式)代表整个单词,如:ID = identification
不要 使用前缀字母

推荐的写法

defaultTimeout

不推荐的写法

kDefaultTimeout

格式化

使用 dartfmt 格式化你的代码。

格式化是一项繁琐的工作,尤其在重构过程中特别耗时。 庆幸的是,你不必担心。 我们提供了一个名为 dartfmt 的优秀的自动代码格式化程序,它可以为你完成格式化工作。 我们有一些关于它适用的规则的 文档 , Dart 中任何官方的空格处理规则由 dartfmt 生成

其余格式指南用于 dartfmt 无法修复的一些规则。

对所有流控制结构使用花括号。

这样可以避免 dangling else (else悬挂)的问题。

if (isWeekDay) {
  print('Bike to work!');
} else {
  print('Go dancing or read a book!');
}

这里有一个例外:一个没有 elseif 语句, 并且这个 if 语句以及它的执行体适合在一行中实现。 在这种情况下,如果您愿意,可以不用括号

if (arg == null) return defaultValue;

但是,如果执行体包含下一行,请使用大括号:

推荐的写法

if (overflowChars != other.overflowChars) {
  return overflowChars < other.overflowChars;
}

不推荐的写法

if (overflowChars != other.overflowChars)
  return overflowChars < other.overflowChars;

欢迎关注微信公众号,文章会首发在微信公众号。
可以搜索公众号名 ‘’杰克的程序人生‘’

查看原文

赞 1 收藏 0 评论 0

million 发布了文章 · 8月27日

一文学会Dart事件循环及异步调用

异步代码在Dart中随处可见。许多库函数返回Future对象,您可以注册处理程序来响应事件,如鼠标单击、文件I/O完成和计时。

本文描述了Dart的事件循环架构,您就可以编写出更好的更少问题的异步代码。您将学习如何使用Future,并且能够预测程序的执行顺序。

<u>注意:本文中的所有内容既适用于原生运行的Dart应用程序(使用Dart虚拟机),也适用于已经编译成JavaScript的Dart应用程序(dart2js的输出)。本文使用Dart一词来区分Dart应用程序和其他语言编写的软件。</u>

在阅读本文之前,你应该熟悉Future和错误处理的基本知识。

基本概念

如果你写过UI代码,你可能已经熟悉了事件循环和事件队列的概念。它们确保了图形操作和事件(如鼠标点击)一次只处理一个。

事件循环和队列

事件循环的工作是从事件队列中获取一个事件并处理它,只要队列中有事件,就重复这两个步骤。

events going into a queue, feeding into an event loop

队列中的事件可能代表用户输入,文件I / O通知,计时器等。 例如,下面是事件队列的图片,其中包含计时器和用户输入事件:

same figure, but with explicit events: 1. key, 2.click, 3. timer, etc.

你可能在其他的语言中熟悉这些。现在我们来谈谈dart语言是如何实现的。

Dart的单线程

一旦一个Dart函数开始执行,它将继续执行直到退出。换句话说,Dart函数不能被其他Dart代码打断。

如下图所示,一个Dart程序开始执行的第一步是主isolate执行main()函数,当main()退出后,主isolate线程开始逐个处理程序事件队列上的所有事件。
在这里插入图片描述

实际上,这有点过于简化了。

dart的事件循环和队列

Dart应用程序的事件循环带有两个队列——事件队列和微任务队列。

事件队列包含所有外部事件:I/O、鼠标事件、绘图事件、计时器、Dart isolate之间的通信,等等。

微任务队列是必要的,因为事件处理代码有时需要稍后完成一个任务,但在将控制权返回到事件循环之前。例如,当一个可观察对象发生变化时,它将几个突变变化组合在一起,并同步地报告它们。微任务队列允许可观察对象在DOM显示不一致状态之前报告这些突变变化。

事件队列包含来自Dart和系统中其他的事件,微任务队列只包含来自Dart核心代码的事件。

如下图所示,当main()函数退出时,事件循环开始工作。首先,它以FIFO(先进先出)顺序执行所有微任务。然后,它使事件队列中的第一项出队并处理,然后它重复这个循环:执行所有微任务,然后处理事件队列上的下一事件。一旦两个队列都为空并且不会再发生任何事件,应用程序的嵌入程序(如浏览器或测试框架)就可以释放应用程序。

<u>注意:如果web应用程序的用户关闭了它的窗口,那么web应用程序可能会在其事件队列为空之前强行退出。</u>

flowchart: main() -&gt; microtasks -&gt; next event -&gt; microtasks -&gt; ...

重要:当事件循环正在执行微任务队列中的任务时,事件队列会卡住:应用程序无法绘制图形、处理鼠标点击、对I/O做出反应等。

尽管可以预测任务执行的顺序,但不能准确预测事件循环何时将任务从队列中移除。Dart事件处理系统基于单线程循环;它不是基于任何类型的时间标准。例如,当您创建一个延迟的任务时,事件将在您指定的时间进入队列。他还是要等待事件队列中它之前的所有事件(包括微任务队列中的每一个事件)全部执行完后,才能得到执行。(延时任务不是插队,是在指定时间进入队列)

提示:链式调用future指定任务顺序

如果您的代码有依赖关系,请以显式的方式编写。显式依赖关系帮助其他开发人员理解您的代码,并且使您的程序更能抵抗代码重构。

下面是一个错误编码方式的例子:

// 因为在设置变量和使用变量之间没有明确的依赖关系,所以不好。
future.then((){...设置一个重要变量...)。
Timer.run(() {...使用重要变量...})。

相反,像这样写代码:

//更好,因为依赖关系是显式的。

future.then(…设置一个重要的变量…)

then((_){…使用重要的变量…});

在使用该变量之前必须先设置它。(如果您希望即使出现错误也能执行代码,那么可以使用whenComplete()而不是then()。)

如果使用变量需要时间并且可以在以后完成,请考虑将代码放在新的Future中:

//可能更好:显式依赖加上延迟执行。

future.then(…设置一个重要的变量…)

then((_) {new Future((){…使用重要的变量…})});

使用新的Future使事件循环有机会处理事件队列中的其他事件。下一节将详细介绍延迟运行的调度代码。

如何安排任务

当您需要指定一些需要延迟执行的代码时,可以使用dart:async库提供的以下API:

Future类,它将一个项目添加到事件队列的末尾。

顶级的scheduleMicrotask()函数,它将一个项目添加到微任务队列的末尾。

使用这些api的示例在下一节中。事件队列:new Future()和微任务队列:scheduleMicrotask()

使用适当的队列(通常是事件队列)

尽可能的在事件队列上调度任务,使用Future。使用事件队列有助于保持微任务队列较短,减少微任务队列影响事件队列的可能。

如果一个任务需要在处理任何来自事件队列的事件之前完成,那么你通常应该先执行该函数。如果不能先执行,那么使用 scheduleMicrotask()将这个任务添加到微任务队列中。

shows chain of event handler execution, with tasks added using Future and scheduleMicrotask().

事件队列: new Future()

要在事件队列上调度任务,可以使用new Future()或new Future.delayed()。这是dart:async库中定义的两个Future的构造函数。

注意:您也可以使用Timer安排任务,但是如果Timer任务中发生任何未捕获的异常,您的应用程序将退出。 相反,我们建议使用Future,它建立在Timer之上,并增加了诸如检测任务完成和对错误进行响应的功能。

要立即将一个事件放到事件队列中,使用new Future():

//在事件队列中添加任务。

new Future((){

 /……代码就在这里……

});

您可以添加对then()或whenComplete()的调用,以便在新的Future完成后立即执行一些代码。例如,当new Future的任务离开队列时,以下代码输出“42”:

new Future(() => 21)
    .then((v) => v*2)
    .then((v) => print(v));

使用new Future.delayed()在一段时间后在队列中加入一个事件:

// 一段时间之后,将事件加入队列
new Future.delayed(const Duration(seconds:1), () {
  // ...代码在这里...
});

尽管前面的示例在一秒后将任务添加到事件队列中,但该任务只有在主isolate空闲、微任务队列为空以及之前在事件队列中入队的任务全部执行完后才能执行。例如,如果main()函数或事件处理程序正在运行一个复杂的计算,则任务只有在该计算完成后才能执行。在这种情况下,延迟可能远不止一秒。

关于future的重要细节:

1 传递给Future的then()方法的函数在Future完成时立即执行。(函数没有进入队列,只是被调用了)

2 如果Future在调用then()之前已经完成,则将一个任务添加到微任务队列,然后该任务执行传递给then()的函数。

3 Future()和Future.delayed()构造函数不会立即完成; 他们将一个项目添加到事件队列。

4 value()构造函数在微任务中完成,类似于#2

5 Future.sync()构造函数立即执行其函数参数,并且(除非该函数返回Future,如果返回future代码会进入事件队列)在微任务中完成,类似于#2。(Future.sync(FutureOr<T> computation())该函数接受一个function参数)

微任务队列:scheduleMicrotask()

async库将scheduleMicrotask()定义为一个顶级函数。你可以像这样调用scheduleMicrotask():

scheduleMicrotask(() {
  // ...代码在这里...
});

由于bug 9001和9002,第一次调用scheduleMicrotask()会将一个创建微任务队列的事件放在事件队列中;此事件创建微任务队列,并将指定给scheduleMicrotask()的函数放入微任务队列,只要微任务队列至少有一个事件,后续对 scheduleMicrotask() 的调用就会正确地添加到微任务队列中。一旦微任务队列为空,下次调用 scheduleMicrotask()时必须重新创建(意味着第一次调用scheduleMicrotask()不会直接进入微任务队列立即执行,会在事件队列上先插入一个创建微任务队列的事件,这个事件还是要在事件队列中排队)。

这些错误的结果是:使用scheduleMicrotask()调度的第一个任务似乎位于事件队列上。

(译者注:dart2.9会将第一次调用scheduleMicrotask()时,将此代码插入事件队列的第一位)

向微任务队列添加任务的一种方法是在已经完成的Future上调用then()。有关更多信息,请参阅前一节(关于future的重要)

必要时使用isolates 或workers

现在您已经阅读了关于调度任务的所有内容,让我们测试一下您的理解。

请记住,您不应该依赖Dart的事件队列实现来指定任务顺序。 实现可能会发生变化,Future的then()和whenComplete()方法是更好的选择。 不过,如果您能正确回答下面这些问题,你学会了。

练习

Question #1

这个示例打印出什么?

import 'dart:async';
void main() {
  print('main #1 of 2');
  scheduleMicrotask(() => print('microtask #1 of 2'));

  new Future.delayed(new Duration(seconds:1),
                     () => print('future #1 (delayed)'));
  new Future(() => print('future #2 of 3'));
  new Future(() => print('future #3 of 3'));

  scheduleMicrotask(() => print('microtask #2 of 2'));

  print('main #2 of 2');
}

答案

main #1 of 2
main #2 of 2
microtask #1 of 2
microtask #2 of 2
future #2 of 3
future #3 of 3
future #1 (delayed)

这个顺序应该你能预料到的,因为示例代码分三批执行:

1 main()函数中的代码

2 微任务队列中的任务(scheduleMicrotask())

3 事件队列中的任务(new Future()或new Future.delayed())

请记住,main()函数中的所有调用都是从头到尾同步执行的。首先main()调用print(),然后调用scheduleMicrotask(),再调用new Future.delayed(),然后调用new Future(),以此类推。只有回调--作为 scheduleMicrotask()、new Future.delayed()和new Future()的参数代码才会在后面的时间执行。

注意:目前,如果注释掉对scheduleMicrotask()的第一个调用,那么对#2和#3的回调将在微任务#2之前执行。这是由于bug 9001和9002造成的,如微任务队列: scheduleMicrotask()中所述。

Question #2

这里有一个更复杂的例子。如果您能够正确地预测这段代码的输出,就会得到一个闪亮的星星。

import 'dart:async';
void main() {
  print('main #1 of 2');
  scheduleMicrotask(() => print('microtask #1 of 3'));

  new Future.delayed(new Duration(seconds:1),
      () => print('future #1 (delayed)'));

  new Future(() => print('future #2 of 4'))
      .then((_) => print('future #2a'))
      .then((_) {
        print('future #2b');
        scheduleMicrotask(() => print('microtask #0 (from future #2b)'));
      })
      .then((_) => print('future #2c'));

  scheduleMicrotask(() => print('microtask #2 of 3'));

  new Future(() => print('future #3 of 4'))
      .then((_) => new Future(
                   () => print('future #3a (a new future)')))
      .then((_) => print('future #3b'));

  new Future(() => print('future #4 of 4'));
  scheduleMicrotask(() => print('microtask #3 of 3'));
  print('main #2 of 2');
}

假设错误9001/9002没有修复,输出如下:

main #1 of 2
main #2 of 2
microtask #1 of 3
microtask #2 of 3
microtask #3 of 3
future #2 of 4
future #2a
future #2b
future #2c
future #3 of 4
future #4 of 4
microtask #0 (from future #2b)
future #3a (a new future)
future #3b
future #1 (delayed)

(译者注)

在这里插入图片描述

这是译者在dart2.9上运行的结果。dart程序会在第一次创建微任务队列时,将创建微任务队列的代码插入到事件队列的第一位,相当于插队。

原作者说的bug已经修复了

总结

你现在应该了解Dart的事件循环以及dart如何安排任务。以下是Dart中事件循环的一些主要概念:

Dart应用程序的事件循环使用两个队列执行任务:事件队列和微任务队列。

事件队列有来自Dart(futures、计时器、isolate messages)和系统(用户操作、I/O等)的事件。

目前,微任务队列只有来自Dart核心代码的事件,如果你想让你的代码进入微任务队列执行,使用scheduleMicrotask()。

事件循环在退出队列并处理事件队列上的下一项之前先清空微任务队列。

一旦两个队列都为空,应用程序就完成了它的工作,并且(取决于它的嵌入程序)可以退出。

main()函数和来自微任务和事件队列的所有项目都运行在Dart应用程序的主isolates 上。

当你安排一项事件时,遵循以下规则:

如果可能,将其放在事件队列中(使用new Future()或new Future.delayed())。

使用Future的then()或whenComplete()方法指定任务顺序。

为了避免耗尽事件循环,请保持微任务队列尽可能短。

为了保持应用程序的响应性,避免在任何一个事件循环中执行计算密集型任务。

要执行计算密集型任务,请创建额外的isolates 或者 workers。

(译者本来想自己总结一篇dart 事件循环和异步使用的文章,不过翻译完这篇文章之后没有这个必要了,这篇文章已经将全部的细节描述清楚了)
[英文文章地址]
(https://dart.cn/articles/arch...:~:text=A%20Dart%20app%20has%20a,queue%20and%20the%20microtask%20queue.&text=First,%20it%20executes%20any%20microtasks,item%20on%20the%20event%20queue.)

学习英语对程序员来说非常必要

查看原文

赞 0 收藏 0 评论 0

million 发布了文章 · 8月24日

Flutter基础——Dart语法

本文主要介绍Dart开发常用的一些语法

基础语法

Final,Const定义常量

Const 变量在编译时就已经固定。
Final 变量或类变量在第一次使用时被初始化,懒加载。
Const不能定义对象 Final能定义对象

例:

//可以省略String这个类型声明final str = "hi world";//final String str = "hi world"; const str1 = "hi world";//const String str1 = "hi world";

定义多行字符串

String str ="""保留换行的字符串可以在编译器里换行""";String str2 ='''保留换行的字符串可以在编译器里换行''';print(str);print(str2);

double类型初始化时可以赋值为int型

double num1 = 1.0;double num2= 1;print(num1.runtimeType);print("----");print(num2.runtimeType);

Map类型取值map[“key”] 赋值同理

Map testMap = {};testMap["flag"] = true;print(testMap["flag"]);

在List类型中使用if语句

var promoActive = true;var nav = [  'Home',  'Furniture',  'Plants',  if (promoActive) 'Outlet'];print(nav);

在List类型中使用For语句

var listOfInts = [1, 2, 3];var listOfStrings = [  '#0',  for (var i in listOfInts) '#$i'];print(listOfStrings);

is关键字判断数据类型

int num = 1;print(num is String);String str = "测试";print(str is String);

~/取整运算符

double num = 5;print( num ~/ 2);//5除2取整

字符串形式输出两个变量

String str1 = "str1";String str2 = "str2";print("$str1 $str2");

flag == true ? 我是true” : ”我是false” 三目运算符

String str = "三目运算符";print(str == "三目运算符"?"我是true":"我是false" );

int.parse(str) String类型转int

num.toString()int类型转String

double.toInt() double类型转int

int num = 1;double doubleNum =1.0;String str = "1";print(int.parse(str).runtimeType);print(num.toString().runtimeType);print(doubleNum.toInt().runtimeType);

isEmpty判断字符串是否为空

String str1 = "";print(str1.isEmpty);String str2 = "1";print(str2.isEmpty);String str3;//此行代码会报错,null不能调用isEmptyprint(str3.isEmpty);

变量或方法前加入下划线表示私有

String _privateStr = "私有变量";_privateFunction(){  print("私有方法");}

import关键字用于实现类导入,as给导入的类起别名

import 'dart:math' as math;

if语句只支持bool类型,不支持直接使用空数据判断

String str;//不合法if(str){}//合法if(str != null){}

List的扩展运算符“...”和空值判断扩展运算符“...?”

"..."支持将一个集合添加到另一个集合里

List list1 = [1,2,3];List list2=[-1,0,...list1];print(list2);List list3;// ...?运算符  如果为空就不插入List list4 = [0,...?list3];print(list4);

级联操作符。通过级联操作符可以对类的内部成员进行链式调用,

级联运算符 (..) 可以实现对同一个对像进行一系列的操作。 除了调用函数, 还可以访问同一对象上的字段属性。

var button = Button() // 获取对象。  ..text = 'Confirm' // 调用成员变量。  ..classes.add('important')  ..onClick.listen((e) => window.alert('Confirmed!'));

第一句创建对象。获取的对象依次执行级联运算符后面的代码, 代码执行后的返回值会被忽略。 上面的代码等价于:

var button = Button();button.text = 'Confirm';button.classes.add('important');button.onClick.listen((e) => window.alert('Confirmed!'));

赋值操作符“??”、“??=”和“~/”及判空操作符“?

String str;print(str ??  1);//如果str为空 返回1str ??= "1"; //如果str为空 将str赋值为1print(str);int num = 1;num?.toString();//如果str为空 不执行toString;print(num.runtimeType);int num1;num1?.toString();//如果str为空 不执行toString;print(num1.runtimeType  == Null);

var 和 dynamic声明变量

Dart属于强类型语言,但可以用var声明变量,Dart对于var声明会自推导出数据类型。实际上var是编译期的语法糖, 而dynamic声明才表示动态类型,dynamic被编译后是一个object类型,在编译期间不对任何的类型进行检查,而是在运行时对类型进行检查。

String 和 int没有默认的类型转换,‘123’不等于123

String str = "1";int num = 1;print(str == num);

指定了数据类型之后不能重新赋值其他类型的数据

 String str = "test"; //此行代码会报错str = 1;

函数相关

函数简写

对于只包含一个表达式的函数,可以使用简写语法

  printFunc(String str)=> print(str) ;//等同于//printFunc(String str) {//  print(str);//}

函数作为参数传递

  _test(){execute(() => print("func被执行了"));execute1(callback);  }  void execute(var func) {func();  }  void execute1(var callback){callback("callback被执行了");  }  callback(str){print(str);  }

可选的命名参数

定义函数时,使用{param1, param2, …},放在参数列表的最后面,用于指定命名参数。例如:

//设置[bold]和[hidden]标志void enableFlags({bool bold, bool hidden}) {// ... }调用函数enableFlags(bold: true, hidden: false);

闭包

Dart 的闭包是定义在方法里面的方法,其特性:
闭包是一个对象
闭包定义在其他方法内部
闭包可以访问外部方法的布局变量,并持有其状态。这是闭包的最大用途,通过闭包的方式把一个方法内部的属性暴露出去,因为方法是可以返回方法的,Dart 中方法也是对象一样可以当做返回值处理

sum(){  var num = 0;  sum2(){num++;print(num);  }  return sum2;    // 也可以这么写,匿名的方式用的最多  return (){num++;print(num);  };}// 多调用闭包几次void main(){  Function a = sum();  a();  a();  a();  a();  a();  a();}~~~~~~~~~~log~~~~~~~~~~123456

可以理解sum()就是一个对象,在初始化对象时,数值a已经被确定为0,再次调用a()时,没有再次执行var num = 0;,因为没有重新初始化类,所以赋值操作不会再执行一次。

「纸上得来终觉浅,绝知此事要躬行。」

查看原文

赞 0 收藏 0 评论 0

million 发布了文章 · 8月21日

深入理解B/S架构

最近看招聘信息很多都要求理解B/S架构,自己面试时也被问到过这个问题,查找了一些资料,整理出这篇博客。

一: B/S的概念

B/S(Brower/Server,浏览器/服务器)模式又称B/S结构,是Web兴起后的一种网络结构模式。
浏览器是客户端最主要的应用软件,客户机上只需要安装一个浏览器,浏览器通过Web Server(网站服务器)同数据库进行数据交互,浏览器负责界面显示逻辑和极少数的事务逻辑,主要的事务逻辑在服务器端实现,将系统功能实现的核心部分集中到服务器上,简化了系统的开发、维护和使用

二:什么是浏览器和服务器:

♦ 浏览器:是阅读和浏览Web的工具,它是通过B/S方式与Web服务器交互信息的。

       一般情况下,浏览器就是客户端,它要求服务器把指定信息传送过来,然后通过浏览器把信息显示在屏幕上。浏览器实际上是一种允许用户浏览Web信息的软件,这些信息是由Web服务器发送出来的。
    ♦ 服务器: 服务器(Server),它既是计算机硬件的称谓,有时又是计算机服务端软件的称谓。
    用户应该区分开它们,主要就是从语境上去区分。
       (1)服务器是一种计算机硬件:服务器应该算是一种高性能的计算机,它作为网络的节点,存储、处理网络上的数据、信息,因此也被称为网络的灵魂。 
       (2)服务器是一种计算机软件:一般IIS服务器、Java服务器、.NET服务器等名词,,服务器实际上是一种连接在Internet上的计算机软件。它负责Web浏览器提交的文本请求。当用户使用计算机上网时,其实是访问服务器硬件。但是,这个服务器硬件上安装了服务器软件,例如IIS服务器、Java服务器、.NET服务器,它们负责接收用户的访问请求,并根据请求,经过计算将数据返回给用户的客户端(浏览器)

三:B/S架构的优点:

1、客户端无需安装,有Web浏览器即可。
2、B/S架构可以直接放在广域网上,通过一定的权限控制实现多客户访问的目的,交互性较强。
3、B/S架构客户端维护和升级方式简单,可以随时更新版本,且无需用户重新下载, 系统的扩展非常容易。
4、B/S结构利用了不断成熟的Web浏览器技术:结合浏览器的多种脚本语言,用通用浏览器实现原来需要复杂专用软件才能实现的强大功能,节约了开发成本。

四:B/S架构的缺点:

1、在跨浏览器上,B/S架构不尽如人意。
2、功能弱化,存在无法实现的功能, 性能相对较低;
3、在速度和安全性上需要花费巨大的设计成本,这是BS架构的最大问题。

五:B/S与C/S的对比

其实B/S架构的缺点都是参照C/S架构来说的,本文不详细介绍C/S架构,简单描述一下
C/S架构是第一种比较早的软件架构,主要用于局域网内。也叫 客户机/服务器模式。
它可以分为客户机和服务器两层:第一层是在客户机系统上结合了界面显示与业务逻辑,第二层是通过网络结合了数据库服务器。简单的说就是第一层是用户表示层,第二层是数据库层。界面表示、事务处理逻辑放在客户端,服务端主要负责数据的存储管理式。
这是一张对比两种架构的图片
在这里插入图片描述
找了很多B/S架构相关的技术知识,梳理了一下学习顺序,整理也是一种再创作,在这个过程中加深了自己对技术的理解。

查看原文

赞 1 收藏 1 评论 0

million 关注了专栏 · 8月20日

JS漫步指南

一入JS深似海,希望这个专栏能在你乘风破浪的旅途中有所帮助。欢迎关注我的公众号:「JS漫步指南」,更多精彩等待发现!

关注 965

million 关注了专栏 · 8月20日

前端巅峰

注重前端性能优化和前沿技术,重型跨平台开发,即时通讯技术等。 欢迎关注微信公众号:前端巅峰

关注 16735

million 关注了专栏 · 8月20日

NoSay

成长路上的一些思考与心得

关注 445

million 关注了专栏 · 8月20日

前端食堂

个人公众号:前端食堂 你的前端食堂,记得按时吃饭~

关注 2053

认证与成就

  • 获得 2 次点赞
  • 获得 2 枚徽章 获得 0 枚金徽章, 获得 0 枚银徽章, 获得 2 枚铜徽章

擅长技能
编辑

(゚∀゚ )
暂时没有

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 2019-09-25
个人主页被 111 人浏览