Typescrip异步函数Promise使用方式
作者:江鸟木又源码分析
Typescrip异步函数Promise
var p = new Promise(function (resolve, reject) { var x = 1; if (x > 0) { resolve({ msg: '异步调用成功', data: { x: 1, y: 2 } }); } else { reject({ msg: '异步调用失败', data: {} }); } }); //异步调用结果 p.then(function (data) { console.log('OK'); console.log(data); //console.log(data.msg,data.data); })["catch"](function (err) { console.log(err); }); //异步函数 function promiseFunc(t) { console.log("".concat(t / 1000, "\u79D2\u540E\u8C03\u7528\u5F02\u6B65")); return new Promise(function (resolve, reject) { setTimeout(resolve, t, '异步调用完成'); }); } //调用异步函数 promiseFunc(1000).then(function (value) { console.log(value); }); var promiseAction = new Promise(function (resolve, reject) { console.log('执行了一些异步操作...'); resolve('异步操作完成了!'); }); promiseAction.then(function (value) { console.log(value); }); // @ts-ignore // const XMLHttpRequest = require('xmlhttprequest').XMLHttpRequest; // let getDataAsync=(url)=>{ // let p = new Promise((resolve, reject) => { // let c =new XMLHttpRequest(); // c.open('GET',url); // c.onreadystatechange = h; // c.responseType = 'json'; // c.setRequestHeader('Accept','application/json'); // c.send(); // function h(){ // if(this.readyState!==4){return;} // if (this.status===200){ // console.log('请求成功返回:',this.status); // resolve(this.response); // }else { // reject(new Error(this.statusText)); // } // } // }); // return p; // }; // getDataAsync('http://192.168.31.180/data.json') // .then(data=>{console.log(data);}) // .catch(err=>{console.log(err);}); //通过https加载json数据 var url = 'https://img-home.csdnimg.cn/data_json/toolbar/toolbar1105.json'; var url1 = 'https://mp-api.iqiyi.com/base/api/1.0/get_role'; var GetJsonData = function (url) { var https = require('https'); https.get(url, function (response) { var data = ''; //数据正在接收中... response.on('data', function (chunk) { data += chunk; }); //数据接收完成 response.on('end', function () { console.log('同步请求数据完成:', JSON.parse(data)); }); }).on("error", function (error) { console.log("Error: " + error.message); }); }; GetJsonData(url); //异步请求JSON数据实现 var GetJsonDataAsync = function (url) { var https = require('https'); return new Promise(function (resolve, reject) { https.get(url, function (response) { var data = ''; //数据正在接收中... response.on('data', function (chunk) { data += chunk; }); //数据接收完成 response.on('end', function () { //console.log(JSON.parse(data)); resolve(data); //数据接收完成 }); }).on("error", function (error) { console.log("Error: " + error.message); reject(new Error(error.message)); }); }); }; //异步调用 GetJsonDataAsync(url).then(function (value) { console.log("======================下面为异步加载数据================================="); if (typeof value === "string") { console.log('异步加载请求数据完成:', JSON.parse(value)); } })["catch"](function (err) { console.log(err); }); //通过request库请求json数据,使用前 sudo npm i -g request安装包 var request = require('request'); request(url, function (error, response, body) { console.error('错误:', error); console.log('状态码:', response && response.statusCode); console.log('数据:', JSON.parse(body)); }); //异步方式 var RequestJsonAsync = function (url) { return new Promise(function (resolve, reject) { request(url, function (e, r, d) { if (null != e) { reject(new Error(e)); } else { resolve(JSON.parse(d)); } }); }); }; RequestJsonAsync(url).then(function (value) { console.log("==============request异步加载json==============================="); console.log(value); })["catch"](function (err) { console.log(err); }); //nodejs needle库使用 ,使用前 npm i needle --save 安装包 var needle = require('needle'); needle.get(url, function (error, response) { if (!error && response.statusCode == 200) console.log(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>", response.body); }); //异步模式 needle('get', url, { json: true }).then(function (resp) { if (resp.statusCode == 200) { console.log(">>>>>>>>>>>>>>异步模式>>>>>>>>>>>>>>>>>>>>>>>>>>>>>", resp.body); } })["catch"](function (err) { console.log(err); }); //使用axios库使用,axios直接异步 使用前安装 npm i -g axios --save var axios = require('axios'); axios.get(url) .then(function (res) { console.log(res); })["catch"](function (err) { console.log(err); }); //axios支持多请求并发 axios.all([ axios.get(url), axios.get(url1) ]).then(axios.spread(function (res1, res2) { console.log(res1); console.log(res2); }))["catch"](function (err) { console.log(err); }); //supertaget库使用 var superagent = require('superagent'); superagent.get(url) .end(function (err, res) { if (err) { return console.log(err); } console.log("superagent库调用==========>", res.body); }); //fetch库使用 使用前安装 npm i node-fetch 3x版本只能import导入 --save 支持异步 // @ts-ignore // import fetch from 'node-fetch'; //不能在模块之外使用 // fetch(url) // .then(res => res.json()) // expecting a json response // .then(json => { // console.log(json); // }) // .catch(err => { // console.log(err); // }); var p1 = new Promise(function (resolve, reject) { resolve('p1 resolve'); }); var p2 = new Promise(function (resolve, reject) { resolve('p2 resolve'); }); //只要p1,p2中的其中一个有状态改变,马上返回pRace var pRace = Promise.race([p1, p2]);//将多个Promise生成一个Promise pRace.then(function (value) { console.log(value); });
typescript- typescrip与react
对象的类型
接口 Interfaces 一般首字母大写
interface Person { readonly id: number; // 只读属性 不能修改 name: string; age: number; age?: number; // 可选 [propName: string]: any; // 任意属性 // 注意, } // 顺序 一般是 只读 -> 默认 -> 可选 -> 任意 let tom: Person = { name: 'Tom', age: 25 };
一旦定义了任意属性,那么确定属性和可选属性的类型都必须是它的类型的子集:
interface Person { name: string; age?: number; [propName: string]: string; } let tom: Person = { name: 'Tom', age: 25, // 报错 需要是 任意属性的子集 gender: 'male' };
任意属性的值允许是 string,但是可选属性 age 的值却是 number,number 不是 string 的子属性,所以报错了
只读的约束存在于第一次给对象赋值的时候,而不是第一次给只读属性赋值的时候
interface Person { readonly id: number; name: string; age?: number; [propName: string]: any; } let tom: Person = { name: 'Tom', gender: 'male' }; tom.id = 89757; // 报错,第一处是在对 tom 进行赋值的时候,没有给 id 赋值。 之后再赋值就报错
数组类型
「类型 + 方括号」表示法
let arr: number[] = [1, 2, 3, 4, 5]; // 表示 是数组, 并且数组元素只能为 number 类型
联合类型和数组的结合
// arr 表示 是数组, 并且数组元素只能为 number 和 string 类型 let arr: (number | string)[] = ['1', 'adaf', 2, 3, 5]
数组泛型
let arr: Array<number> = [1, 2, 3, 4, 5];
类数组
常见的类数组都有自己的接口定义,如 IArguments, NodeList, HTMLCollection 等
// error 函数中的 arguments 这也定义会报错 let args: number[] = arguments; // success 改成 let args: IArguments = arguments;
函数类型
函数声明
function sum(x: number, y: number): number { return x + y; }
函数表达式
let mySum = function (x: number, y: number): number { return x + y; };
这是可以通过编译的,不过事实上,上面的代码只对等号右侧的匿名函数进行了类型定义,而等号左边的 mySum,是通过赋值操作进行类型推论而推断出来的。如果需要我们手动给 mySum 添加类型,则应该是这样:
let mySum: (x: number, y: number) => number = function (x: number, y: number): number { return x + y; };
类型断言
function getLength(something: string | number): number { // 将他断言为 string if ((<string>something).length) { return (<string>something).length; } else { return something.toString().length; } }
类型断言不是类型转换,断言成一个联合类型中不存在的类型是不允许的:
// error function toBoolean(something: string | number): boolean { return <boolean>something; } // Type 'number' is not comparable to type 'boolean'
内置对象
ECMAScript 的内置对象
Boolean、Error、Date、RegExp 等。
DOM 和 BOM 的内置对象
Document、HTMLElement、Event、NodeList 等。
泛型
是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性。
function swap<T, U>(tuple: [T, U]): [U, T] { return [tuple[1], tuple[0]]; }
问题
type 和 Interfaces 的区别
React 中使用
import React, { MouseEvent, ReactNode } from 'react' type Props = { onClick(e: MouseEvent<HTMLElement>): void children?: ReactNode } const Button = ({ onClick: handleClick, children }: Props) => ( <button onClick={handleClick}>{children}</button> )
高阶组件的使用方式
import React from 'react'; import { Form } from 'antd'; import { FormComponentProps } from 'antd/es/form'; interface IProps extends FormComponentProps { loading: boolean; } const Page: React.FC<IProps> = (props) => { return ( <div>Page</div> ) } export default Form.create<IProps>()(Page)
对象类型取值
type DealWithProps = { onClose: Function; eventId: string; dealTime: any; }; // 处理 const getOnDealWith = (parameObj: DealWithProps) => () => { // TODO 暂无接口 for (let i in parameObj) { console.log(parameObj[i], typeof parameObj[i]); // 报错 /** Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'DealWithProps'. No index signature with a parameter of type 'string' was found on type 'DealWithProps'.ts(7053) **/ } };
Event 事件对象类型
常用 Event 事件对象类型:
ClipboardEvent<T = Element>
剪贴板事件对象DragEvent<T = Element>
拖拽事件对象ChangeEvent<T = Element>
Change 事件对象KeyboardEvent<T = Element>
键盘事件对象MouseEvent<T = Element>
鼠标事件对象TouchEvent<T = Element>
触摸事件对象WheelEvent<T = Element>
滚轮事件对象AnimationEvent<T = Element>
动画事件对象TransitionEvent<T = Element>
过渡事件对象
Promise 类型
Promise<T> 是一个泛型类型,T 泛型变量用于确定使用 then 方法时接收的第一个回调函数(onfulfilled)的参数类型。
interface IResponse<T> { message: string, result: T, success: boolean, } async function getResponse (): Promise<IResponse<number[]>> { return { message: '获取成功', result: [1, 2, 3], success: true, } } getResponse() .then(response => { console.log(response.result) })
使用 react-router-dom
withRouter(ModuleLayout)
这类高阶组件的使用 方式,通常都需要继承到高阶组件 的 类型定义,如 antd Form.create 需要继承 FormComponentProps
import { FormComponentProps} from 'antd/lib/form/Form'; import { withRouter, Link, RouteComponentProps } from 'react-router-dom'; interface IProps extends RouteComponentProps<any> { basePath: string; menuList: IMenuItem[]; children: JSX.Element; } function App(props: IProps){ // 因为继承了 RouteComponentProps 所以可以获取 location const {location} = props; // xx } export default withRouter(ModuleLayout);
react 定义方法 useApi
/* eslint-disable no-param-reassign */ import { useRef, useEffect } from '@alipay/bigfish/react'; import { useImmer } from 'use-immer'; type Service<T> = () => Promise<T>; type DependencyList = ReadonlyArray<any>; interface Option<T> { onSuccess?: (data: T) => void; onError?: (errMsg: string) => void; } interface State { data: any; loading: boolean; } interface ResponseData<T> { success: boolean; data: T; message: string; } export default function useApi<T>( service: Service<ResponseData<T>>, dependencyList: DependencyList = [], option?: Option<T> ) { const initialState: State = { data: undefined, loading: false, }; const [state, setState] = useImmer(initialState); const serviceId = useRef(0); useEffect(() => { const currentId = serviceId.current; setState(draft => { draft.loading = true; draft.data = null; }); // 异步方法 service().then(data => { // 组件卸载不更改 if (currentId !== serviceId.current) { return; } if (!data.success) { // 错误 setState(draft => { draft.loading = false; draft.data = null; }); if (option && option.onError) { option.onError(data.message); } } else { // 成功 setState(draft => { draft.loading = false; draft.data = data.data; }); if (option && option.onSuccess) { option.onSuccess(data.data); } } }); return () => { serviceId.current += 1; }; }, dependencyList); return [state, setState]; }
react 使用 useRef
import React, { useRef, useState, FC } from '@alipay/bigfish/react'; import { message } from 'antd'; interface IProps extends FormComponentProps { isShow:boolean; } interface IUseRef extends HTMLDivElement { getAssets: Function; } const CreateStep: FC<IProps> = props => { const { form, isShow } = props; const refList = useRef<IUseRef>(null); const [current, setCurrent] = useState(1); function setNextStep() { const { current } = refList; const { getAssets } = current; // 类型“IUseRef | null”上不存在属性“getAssets” } return <div ref={refList}>xxxx</div>; }; export default CreateStep;
上面定义泛型的时候, 这个写法会报一个错误,因为 current 开始会是空的,后面 dom 挂在之后才会获取实例。
看下 useRef 源码定义, 也就是泛型的值就是 current 的结果
interface RefObject<T> { readonly current: T | null; } function useRef<T>(initialValue: T|null): RefObject<T>;
并且 ref={ refLise } 需要传入的是 dom 元素节点,所以通过继承 HTMLDivElement 来得到 useRef 泛型。
interface IUseRef extends HTMLDivElement { getAssets: Function; }
所以解决的方法。
方法一: 类型断言, 但是这样的代码其实是不严警的
const { getAssets } = current as IUseRef;
方法二: 通过 if 判断,消除 current 为 null 的时候,如果不为 null 这里的类型就必定是 IUseRef 类型,所以可以取出 getAssets
let getAssets ; if(current && current.getAssets){ getAssets = current.getAssets; }
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。