react-testing-library

任意目录下的 *.test.js

__tests__ 目录下的 *.js

import React from 'react'
import {renderIntoDocument, cleanup} from 'react-testing-library'
import App from '../App'
import * as jestDom from 'jest-dom'

expect.extend(jestDom)

afterEach(cleanup)

test('react-testing-library works!', () => {
  const {container} = renderIntoDocument(<App name="Jill" />)
  expect(container.firstChild).toHaveTextContent('Hello Jill!')
})
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import registerServiceWorker from './registerServiceWorker';

ReactDOM.render(<App name="Lizhe"/>, document.getElementById('root'));
registerServiceWorker();

npm run test XXXXXX.test.js

Formik 总结

以下内容总结自Formik的官方文档, 所以你会看到一些原文引用(当然是我过滤后的)

Overview

Formik 总的来说帮助我们做下面3件事

  1. Getting values in and out of form state
  2. Validation and error messages
  3. Handling form submission

Why not Redux-Form?

总的来说, redux form提供的东西太重了, 过多的reducer, 而且通常情况下我的Form值并不需要保存在redux的state中

My goal with Formik was to create a scalable, performant, form helper with a minimal API that does the really really annoying stuff and leaves the rest up to you.

Formik 使用以下3个event handlers

handleChange, handleBlur, and handleSubmit 

handleChange and handleBlur work exactly as expected–they use a name or id attribute to figure out which field to update.

也就是说Formik需要使用name或者是id来确认是哪个field被change了

以下两种方式使用Formik都是可以的

There are two ways to use Formik:

  • withFormik(): A Higher-order Component (HoC) that accepts a configuration object
  • <Formik />: A React component with a render prop

正常来说你可以使用下面这些常用属性

  values,

  errors,

  touched,

  handleChange,

  handleBlur,

  handleSubmit,

  isSubmitting,

如果你向我一样, 想传入一些自定义的变量, 你可要看仔细了

自定义的form应该是这样的

import React from "react";

import Alert from "./Alert";

 

const PlayerForm = ({

values,

handleSubmit,

iWantTransferthisValue

}) => {

console.log("here:")

console.log(iWantTransferthisValue)

return (

<form className="form" onSubmit={handleSubmit}>

{iWantTransferthisValue}

</form>

);

};

 

export default PlayerForm;

下面是对上面form的调用

const EditPlayerForm = props => <PlayerForm {...props} isNewPlayer={false} iWantTransferthisValue={props.wantedValue}/>;
<Formik onSubmit={onSubmit} initialValues={player} iWantTransferthisValue="testValue">

            {x => <EditPlayerForm {...x} iWantTransferthisValue={'great!'} /> }

</Formik>

submit方法接收两个参

第一个是values, 保存form的值, 第二个参数是扩展参数, 这里用结构拆开, 包含了一些回调函数

const onSubmit = async (values, { props, setSubmitting, setErrors }) => {
    await api.edit(values).then(
      values => {
        console.log(values),
        setSubmitting(false),
        history.push("/players")
      },
      error => {
        setSubmitting(false);
        setErrors({ general: error.message });
      });
  };

如果使用Field 标签, 还可以写的更简单一些, 下面的input标签和Field标签等价

<input

              type="text"

              name="social.facebook"

              onChange={handleChange}

              onBlur={handleBlur}

              value={values.social.facebook}

/>
<Form>

            <Field type="email" name="email" />

</Form>

如果需要给form中的fileds赋初始值

<Formik

      initialValues={{ name: 'jared' }}

</Formik>

react-intl

Internationalize React apps. This library provides React components and an API to format dates, numbers, and strings, including pluralization and handling translations.

react intl 用于格式化日期, 数字, 字符串多元化和翻译(实际上可以用做国际化)

安装

$ npm install react-intl --save
import React, {Component} from 'react';
import ReactDOM from 'react-dom';
import {IntlProvider, FormattedMessage} from 'react-intl';
 
class App extends Component {
    constructor(props) {
        super(props);
        this.state = {
            name       : 'Eric',
            unreadCount: 1000,
        };
    }
 
    render() {
        const {name, unreadCount} = this.state;
 
        return (
            <p>
                <FormattedMessage
                    id="welcome"
                    defaultMessage={`Hello {name}, you have {unreadCount, number} {unreadCount, plural,
                      one {message}
                      other {messages}
                    }`}
                    values={{name: <b>{name}</b>, unreadCount}}
                />
            </p>
        );
    }
}
 
ReactDOM.render(
    <IntlProvider locale="en">
        <App />
    </IntlProvider>,
    document.getElementById('container')
);

ES6 拾遗(9)

Module

ES6模块不是对象, 而是通过export命令显示指定输出的代码, 再通过import命令输入

ES6 模块

import { stat, exists, readFile } from ‘fs’;

上面代码的实质是从fs模块加载3个方法, 而不加载其他方法. 
这种加载被称为 编译时加载 或者 静态加载, 即ES6 可以在编译时就完成模块加载, 效率比CommonJS模块的加载方式高. 
当然这也导致了ES6模块本身无法被引用, 因为它不是对象

export命令

模块功能主要又两个命令构成 export和import.

export命令用于规定模块的对外接口, import命令用于输入其他模块提供的功能.

一个模块就是一个独立的文件. 该文件内部的所有变量, 外部无法获取. 如果希望外部能够读取内部某个变量, 就必需使用export关键字输出改变量

export var firstName = 'Michael'

export var lastName = 'Jackson'

export var year = '1958'

等价于

var firstName = 'Michael'

var lastName = 'Jackson'

var year = '1958'

export { firstName, lastName, year }

export 命令除了输出变量, 还可以输出函数或者类

export function add( a,b ){

    return a+b;

}

通常情况, export输出的变量就是本来的名字, 但是可以使用as关键字重命名

export {

    v1 as streamV1,

    v2 as streamV2

}

import 命令

使用export命令定义了模块的对外接口以后, 其他js文件就可以通过import命令加载这个模块了

export default 命令

使用import命令时用户需要知道所要加载的变量名或者函数名, export default命令可以为模块指定默认输出, 用户就可以不用知道具体名称了

ES6 拾遗(8)

Class的基本用法

1. class表达式

直接通过class定义肯定没有错

class也可以使用表达式的形式定义, 类似函数

下面的代码中, 这个类的名字是MyPoint而不是Point, Point只在class内部的代码可用, 代指当前类

2. 类不存在变量提升,需要在定义之后才能使用

3. this的指向

类的方法内部如果含有this, 它将默认指向类的实例. 但是必须非常小心, 一旦单独使用该方法, 很可能会报错

const MyPoint = class Point{

  hello(){

    this.sayName("Lizhe");

  }

  sayName(name){

    console.log(name)

  }

};

let p = new MyPoint();

p.hello();

如果将方法hello单独导出并且使用会出现错误, 原因在于此时this的指向发生了改变, 所以此时的this不包含sayName

一个比较简单的解决方案是在构造函数中使用bind

4. setter 和 getter

ES6 明确规定, class内部只有静态方法, 没有静态属性

const MyPoint = class Point{

 

  get myname() {

    console.log("getter:"+this._myname)

    return this._myname;

  }

  set myname(value) {

    console.log("setter:"+value)

    this._myname = value;

  }

};

 

var p = new MyPoint();

p.myname = "Lizhe"

p.myname

下面这个例子更清晰一些, myname是变量, setMyName和getMyName均为方法,
不过比较奇葩的是set方法的调用是通过等号传参
get方法的调用不需要调用符()

Class的继承

class可以通过extends关键字实现继承

super在这里表示构造函数, 用来新建父类的this对象

子类必需在constructor方法中调用super方法, 否则新建实例时会报错, 这是因为子类没有自己的this对象, 而是继承父类中的this对象, 然后对其进行加工
如果不调用super方法, 子类就得不到this对象

super关键字

super这个关键字既可以当做函数使用, 也可以当做对象使用. 在这两种情况下, 它的用法完全不同

1. 第一种情况, super作为函数调用时, 代表父类的构造函数. ES6 要求, 子类的构造函数必需执行一次super函数
作为函数时, super() 只能用在子类的构造函数之中, 用在其他地方就会报错

2. 第二种情况, super作为对象时在普通方法中指向父类的原型对象; 在静态方法中指向父类
由于super指向父类的原型对象, 所以定义在父类实例上的方法或属性是无法通过super调用的

ES6 拾遗(7)

async函数

ES2017 标准引入了async函数, 使得异步操作变得更加方便. async函数是什么, 用一句话来说, 它就是Generator函数的语法糖

async函数返回一个Promise对象, 可以使用then方法添加回调函数. 当函数执行时, 一旦遇到await就会先返回Promise对象, 等到异步操作完成, 再接着执行函数体内后面的语句

function timeout(ms){

  return new Promise((resolve)=>{

    setTimeout(resolve,ms)

  })

}

 

async function asyncPrint(value,ms){

  await timeout(ms);

  console.log(value);

}

 

let theReturnOfAsync = asyncPrint("hello world",2000);

console.log(theReturnOfAsync);

在上面的例子中可以看到async函数返回了一个Promise对象, 而await命令后面是一个Promise对象. 如果不是, 会被转成一个立即resolve的Promise对象

async 函数返回一个Promise对象
async函数内部return语句返回的值, 会成为then方法回调函数的参数, async函数内部抛出的错误会导致返回的Promise对象变为reject状态, 抛出的错误对象会被catch方法回调函数接收到

Promise对象的状态变化

async函数返回的Promise对象必需等到内部所有await命令后面的Promise对象执行完毕才会发生状态改变, 除非遇到return语句或者抛出错误.
也就是说, 只有async函数内部的异步操作执行完, 才会执行then方法指定的回调函数

await命令

正常情况下, await命令后面是一个Promise对象. 如果不是, 会被转为一个立即resolve的Promise对象
只要一个await语句后面的Promise变为reject, 那么整个async函数都会被中断
有时, 我们希望即使前一个异步操作失败, 也不要中断后面的异步操作. 这时可以将第一个await放在try…catch结构里面, 这样不管这个异步操作是否成功, 第二个await都会执行

另一个方法是在await后面的Promise对象后直接添加一个catch方法, 处理前面的错误

async function asyncPrint(value,ms){

    await timeout(ms).catch(e => console.log(e));

    console.log(value);

}

请求的发生时机

一开始我想当然的认为ES6中的await后面的方法在触发之后, resolve之前, 会继续执行下一个await, 和java的feature.get()一样

如果第一个feature.get() 耗时 10秒, 第二个耗时11秒, 那么第二个feature会在第一个第一个feature结束后1秒结束

然而ES6中的await并不是这样, 下面的代码能说明这个问题

每个await方法耗时20秒, 第二个会在第一个结束20秒之后才结束,一共耗时40秒

function timeout(ms){

  return new Promise((resolve)=>{

    setTimeout(resolve,ms)

  })

}

 

async function asyncPrint(value){

  console.log("start"); // 0 second

  await timeout(20000); // 20 seconds

  console.log(value);

  await timeout(20000); // 40 seconds

  console.log("end")

}

 

let theReturnOfAsync = asyncPrint("hello world");

console.log(theReturnOfAsync);

稍微做一下改动可以得到一个带返回值的版本

function timeout(ms,value) {

  return new Promise(resolve => {

    setTimeout(resolve, ms, value);

  });

}

 

async function asyncPrint(value) {

  console.log("start"); // 0 second

  await timeout(2000,"test1").then((value)=>console.log(value)); // 2 seconds

  await timeout(2000,"test2").then((value)=>console.log(value)); // 4 seconds

  console.log("end");

}

 

let theReturnOfAsync = asyncPrint("hello world");

console.log(theReturnOfAsync);

使用Promise.all 方法可以保证同时出发多个Promise对象

function timeout(ms,value) {

  return new Promise(resolve => {

    setTimeout(resolve, ms, value);

  });

}

 

async function asyncPrint(value) {

  console.log("start"); // 0 second

  Promise.all([timeout(5000,"test1"), timeout(2000,"test2"), timeout(2000,"test3")]).then(function(values) {

    console.log(values);  // 5 seconds

  });

  console.log("end");

}

 

let theReturnOfAsync = asyncPrint("hello world");

console.log(theReturnOfAsync);

第二种同时触发Promise的写法, 需要注意的是
await 关键字后面如果是Promise对象, 就会造成阻塞, 例如
await getPromise() 就会造成阻塞, 因为getPromise()会返回一个Promise对象, 那么下一个await语句就会被迫等到第一个await返回之后才会被触发
下面的写法中因为先进行了三次调用, 然后await promise, 核心点在于, 同时出发3个请求, 然后对promise对象进行await

function timeout(ms,value) {

  return new Promise(resolve => {

    setTimeout(resolve, ms, value);

  });

}

 

async function asyncPrint(value) {

  console.log("start"); // 0 second

  let test1 = timeout(5000,"test1");

  let test2 = timeout(7000,"test2");

  let test3 = timeout(9000,"test3");

  

  let test1_return = await test1;

  // 5 seconds

  console.log(test1_return);

 

  let test2_return = await test2;

  // 7 seconds

  console.log(test2_return);

 

  let test3_return = await test3;

  // 9 seconds

  console.log(test3_return);

  

  console.log("end");

}

 

let theReturnOfAsync = asyncPrint("hello world");

console.log(theReturnOfAsync);

ES6 拾遗(6)

对象的扩展

1. ES6 允许直接写入变量和函数作为对象的属性和方法

当属性名是表达式时


let obj = {
      ['a'+'b'+'c']: 123
}

ES6 允许在对象中只写属性名, 不写属性值, 这时 属性值等于属性名所代表的变量,这种写法用于函数的返回值会非常方便

2. Object.assign()

Object.assign 方法用于将源对象的所有可枚举属性复制到目标对象

Object.assign( target, source1, source2)

同名属性, 后边的覆盖前边的

需要注意的是Object.assign进行的浅克隆, 而不是深克隆

3. Object.keys()

ES5 引入了Object.keys 方法, 返回一个数组, 成员是参数对象自身的(不含继承的) 所有可遍历(enumerable)属性的键名

Object.values() 方法返回一个数组, 成员是参数对象自身的(不含继承的) 所有可遍历属性的值

Object.entries 方法返回一个数组, 成员是参数对象自身的(不含继承的)所有可比遍历属性的键值对数组

4. 对象的扩展运算符

解构赋值 let { x, y, …z } = { x:1, y:2, a:3, b:4}

解构赋值必需是最后一个参数

解构赋值的复制也是浅克隆

Set 和 Map 数据结构

Set

ES6 提供了新的数据结构—- Set , 类似于数组, 但是成员的值都是唯一的, 没有重复
Set本身是一个构造函数, 用来生成Set数据结构

const s = new Set();

[ 2, 3, 5, 2, 2 ].forEach( x => s.add(x) );

————————

const set = new Set( [1, 2, 3, 4, 4] );

Set也支持keys(), values(), entries(), 不过因为Set没有key值, 所以keys和values 行为是一样的

Map

JavaScript 的对象 ( Object ) 本质上是键值对的集合( Hash 结构 ), 但是只能用字符串作为键, 这给它的使用带来了很大限制

为了解决这个问题, ES6 提供了 Map数据结构. 它类似于对象, 也是键值对的集合, 但是”键” 的范围不限于字符串, 各种类型的值都可以当做键

通过构造函数初始化, Map可以接受一个数组作为参数. 该数组的成员是一个个表示键值的数组

以及一些常用方法

注意: 只有对同一个对象的引用, Map结构才将其视为同一个键

与其他数据结构互相转换

1. Map 转为数组

Map转为数组最方便的方法就是使用扩展运算符( … )

2. 数组转为Map

将数组传入Map构造函数就可以转为Map

3. Map转为对象

const map = new Map(

    [

        [1,"number 1"],

        [2,"number 2"]

    ]

);

 

function strMapToObj(strMap){

    let obj = Object.create(null);

    for(let [k,v] of strMap){

        obj[k] = v

    }

    return obj

}

 

console.log(strMapToObj(map));

4. 对象转Map

let obj = {

    a:1,

    b:2,

    c:3

}

function objToStrMap(obj){

    let map = new Map();

    for(let k of Object.keys(obj)){

        map.set(k, obj[k]);

    }

    return map;

}

 

console.log(objToStrMap(obj));

5. Map 转 JSON 对象

Map转为JSON要区分两种情况, 如果Map的键名都是字符串, 这时可以选择转为对象JSON

const map = new Map([[1, "number 1"], [2, "number 2"]]);

 

function strMapToObj(strMap) {

  let obj = Object.create(null);

  for (let [k, v] of strMap) {

    obj[k] = v;

  }

  return obj;

}

 

function strMapToJson(strMap) {

  return JSON.stringify(strMapToObj(strMap));

}

 

console.log(strMapToJson(map))

6. JSON 转为 Map

function objToStrMap(obj) {

  let map = new Map();

 

  for (let k of Object.keys(obj)) {

    map.set(k, obj[k]);

  }

 

  return map;

}

 

function jsonToStrMap(jsonStr) {

  return objToStrMap(JSON.parse(jsonStr));

}

 

let jsonStr = '{"name":"lizhe"}';

console.log(jsonToStrMap(jsonStr));

ES6 拾遗(5)

数组的扩展

1. 扩展运算符( spread ) 是三个点 ( … ), 它如同rest参数的逆运算, 将一个数组转为用逗号分隔的参数序列

console.log( ...[1,2,3])
console.log( 1,2, ...[3,4,5], 6 )

2. 合并数组

扩展运算符提供了数组合并的新方法

ES5
[ 1,2 ] .contact( more )

ES6
[ 1,2 ...more ]

3. Array.from()

Array.from方法用于将两类对象转为真正的数组

实际应用中, 常见的类似数组的对象是DOM操作返回的NodeList和函数内部的arguments对象, Array.from都可以将它们转为真正的数组

//NodeList对象

let ps = document.querySelectorAll('p');

Array.from(ps).forEach( function(p) ){
      console.log(p);
});

// arguments 对象

function foo(){
      var args = Array.from(arguments);
}

只要是部署了Iterator接口的数据结构, Array.from都能将其转为数组

4. Array.of()

Array.of 方法用于将一组值转换为数组

Array.of( 3,11,8 )

5. 数组实例的copyWithin()

数组实例的copyWithin方法会在当前数组内部将指定位置的成员复制到其他位置, 然后返回当前数组.

也就是说, 使用这个方法会修改当前数组

target: 从该位置开始替换
start: 从该位置开始读取数据,默认为0, 复数表示倒数
end: 到该位置前停止读取数据,默认等于数组长度

[1,2,3,4,5].copyWithin(0,3)      // [4,5,3,4,5]

6. 数组实例的 find() 和 findIndex()

数组实例的find方法用于找出第一个符合条件的数组成员.
它的参数是一个回调函数, 所有数组成员依次执行这个回调函数, 直到找到第一个返回值为true的成员, 然后返回该成员, 如果没有符合条件的成员, 则返回undefined.

[ 1,2,3,4 ].find( (n) => n>1 )
// 2

find方法的回调函数可以接受3个参数, 依次为 当前的值, 当前的位置 和 原数组

findIndex方法的用法和find类似, 返回第一个成员的位置

7. 数组实例的fill()

fill方法使用给定的值填充一个数组

new Array(3).fill(7)     //[ 7,7,7 ]

第二个参数和第三个参数可以用于指定填充的起始位置和结束位置

8. 数组实例的entries(), keys() 和 values()

ES6 提供了3个新方法用于遍历数组. 它们都返回一个遍历器对象, 可以用for…of 循环遍历, 唯一的区别在于keys是对键名遍历, values对值遍历, entries对键值对遍历

9. 数组实例的includes()

includes 方法返回一个布尔值, 表示某个数组是否包含给定的值

10. 数组的空位

空位不等于undefined, 空位没有任何值 [, , , ]

ES6 拾遗(4)

箭头函数

ES6 允许使用”箭头” => 来定义函数

var f = argument => argument +1

上面的代码等价于

var f = function ( argumenrt ) {

    return argument + 1

}

如果箭头函数不需要参数或需要多个参数, 就使用圆括号代表参数部分
var f = () => 5

如果箭头函数的代码块部分多于一条语句, 就要使用大括号将其括起来, 并使用return语句返回
var sum = (a,b) => {return a+b}

由于大括号被解释为代码块, 所以如果需要箭头函数直接返回一个对象, 必需在对象外面加上括号
var getTemp = (id) => ({ id:id, name:”lizhe”})

箭头函数可以与变量解构一起使用
const getTemp = ({ id, name }) => id + “of ” +name

注意事项

1. 函数体内的this对象就是定义时所在的对象, 而不是使用时所在的对象

2. 不可以当做构造函数. 也就是说, 不可以使用new命令, 否则会抛出一个错误

3. 不可以使用arguments对象, 该对象在函数体内不存在. 如果要用, 可以用rest参数代替 (…values)

4. 不可以使用yield命令, 因此箭头函数不能用作Generator函数

需要特别注意的是第一项, this对象的指向是可变的, 但在箭头函数中它是固定的 ( 使用ajax回调函数时, 可以使用箭头函数来代替 _this )

嵌套的箭头函数

箭头函数内部还可以再使用箭头函数 

箭头函数

箭头函数则会捕获其所在上下文的this值,作为自己的this值,使用箭头函数就不用担心函数内的this不是指向组件内部了。可以按下面这种方式使用箭头函数:

class Test extends React.Component {
    constructor (props) {
        super(props)
        this.state = {message: 'Allo!'}
    }

    handleClick (e) {
        console.log(this.state.message)
    }

    render () {
        return (
            <div>
                <button onClick={ ()=>{ this.handleClick() } }>Say Hello</button>
            </div>
        )
    }
}

ES6 拾遗(3)

Promise 对象

所谓promise, 简单来说是一个容器, 里面保存着某个未来才会结束的事件

有 3 种状态 Pending 进行中, Fulfilled 已成功, Rejected 已失败

ES6规定, Promise对象是一个构造函数, 用来生成Promise实例.

function test(){

  let value = "hello world";

  var promise = new Promise(

    function(resolve,reject){

      if(true){

        resolve(value)

      }else{

        reject(value)

      }

    }

  );

}

test();

Promise 构造函数接受一个函数作为参数, 该函数的两个参数分别是resolve和reject

它们是两个函数, 由JavaScript引擎提供, 不用自己部署

resolve 函数的作用是, 将Promise对象的状态从"未完成" 改为 "成功", 即从Pending改为Resolved

在异步操作成功时调用, 并将异步操作的结果作为参数传递出去

reject 函数的作用是, 将Promise对象的状态从"未完成" 改为 "失败", 即从Pending改为Rejected

在异步操作失败时调用, 并将异步操作报出的错误作为参数传递出去

Promise 实例生成以后, 可以用then方法分别指定Resolved 状态和Rejected状态的回调函数

then 可以接受两个回调函数作为参数

第一个回调函数是Promise对象的状态变为Resolved时调用, 第二个回调函数是Promise对象的状态变为Rejected时调用.

其中第二个函数是可选的,不一定要提供

function test(){

  let value = "hello world";

  var promise = new Promise(

    function(resolve,reject){

      if(false){

        resolve(value)

      }else{

        reject("error message")

      }

    }

  );

  promise.then((value)=>{

    console.log(value)

  },(error)=>{

    console.log(error)

  })

}

test();

Promise实例的状态变为Resolved之后, 就会触发then方法绑定的回调函数

如果调用resolve函数和reject函数时带有参数, 那么这些参数会被传递给回调函数, reject函数的参数通常是error对象的实例, 表示抛出错误;

Promise.all()

Promise.all 方法用于将多个Promise实例包装成一个新的Promise实例

var p = Promise.all()

Promise.all 方法接受一个数组作为参数, p1, p2, p3 都是Promise对象的实例, 如果不是则会先调用Promise.resolve方法, 将参数转化为Promise实例再进一步处理

Promise.all 方法的参数不一定是数组, 但是必须有Iterator接口, 且返回的每个成员都是Promise 实例

p的状态由p1, p2, p3 决定, 分为两种情况

1. 只有p1, p2, p3的状态都变成 Fulfilled, p 的状态才会变成Fulfilled, 此时 p1, p2, p3 的返回值组成一个数组, 传递给p的回调函数

2. 只要p1, p2, p3中有一个被Rejected, p 的状态就会变成Rejected, 此时第一个被Rejected的实例的返回值会传递给p的回调函数

Promise.race()

Promise.race 方法同样是将多个Promise实例包装成一个新的Promise实例

var p = Promise.race( [ p1, p2, p3 ] )

只要p1, p2, p3 中有一个实例率先改变状态, p 的状态就跟着改变, 那个率先改变的Promise实例的返回值就传给p的回调函数

使用这个方法可以定义超时

Promise.resolve()

Promise.resolve方法可以将现有对象转化为Promise对象.

var p = Promise.resolve($.ajax(‘/test.json’));

1. 参数是一个Promise实例

如果参数是Promise实例, 那么Promise.resolve 将不做任何修改, 原封不动地返回这个实例

2. 参数是一个thenable对象

thenable对象指的是具有then方法的对象

let thenable = {

    then: function(resolve, reject) {

        resolve(42)

    }

}

Promise.resolve 方法会将这个对象转化为Promise对象, 然后立即执行thenable对象的then方法.

3. 参数不是具有then方法的对象或根本不是对象

如果参数是一个原始值, 或者是一个不具有then方法的对象, 那么Promise.resolve方法返回一个新的Promise对象, 状态为Resolved

上面代码中生成一个新的Promise对象的实例p1.

由于字符串不属于异步操作(判断方法是字符串对象不具有then方法), 返回promise实例的状态从生成起就是Resolved, 所以回调函数会立即执行.

Promise.resolve 方法的参数会同时传给回调函数

4. 不带有任何参数

Promise.resolve 方法允许在调用时不带有参数, 而直接返回一个Resolved状态的对象.

所以如果希望得到一个Promise对象, 比较方便的方法就是直接调用Promise.resolve方法
 

Promise.reject()

Promise.reject(reason) 方法也会返回一个新的Promise实例, 状态为 Rejected

var p = Promise.reject(‘出错了’);