三大组成部分:ECMAScript;DOM(文档对象模型):对文档的操作;BOM(浏览器对象模型):主要包括对浏览器的相关操作;
let和const let、const和var的区别 var的特性 1、var可以重复声明;
2、var作用域:全局作用域和函数作用域;
3、会进行预解析,但可能造成代码混乱;
let的特性 1、let在同一作用域下不能进行重复声明;
2、let作用域:全局作用域和块级作用域;if、while、for等有{}的语句,let会考虑在花括号之间的为块级作用域,仅在代码块内部起作用;(与C++类似)
3、不进行预解析;JS有预解析机制,在下面声明而在上面去调用并不会报错,然而使用let声明时不会进行预解析;
const的特性 1、const为常量,声明时必须赋值,而之后并不能对其进行修改;
2、其他情况与let一样,不能重复声明、不能预解析、为块级作用域。
块级作用域 之前在写代码时,为了避免与全局冲突,一般会先声明一个匿名函数,并马上对其调用;
有了块级作用域之后,可以直接用{}来生成代码块,便能直接在代码块里添加代码;
如果在循环之中添加了一个块级作用域,将要如何去处理,
1 2 3 4 5 6 7 8 9 { let lis = document .querySelectorAll("li" ); for (let i = 0 ; i < lis.length; i++){ lis[i].onclick = function ( ) { console .log(i); } } }
解构赋值 对象的解构赋值 对象可以有若干属性,且每个属性方法衷都可以存储数据;当你希望用其他的变量将原变量的属性存储起来,原本的JS写法如下:
1 2 3 4 5 6 let obj = { a: 1 , b: 2 }; let a = obj.a;let b = obj.b;
在ES6的标准下,可以把声明改成这样:
1 2 3 4 5 6 let obj = { a: 1 , b: 2 }; let {a, b, c} = obj;
数组的解构赋值 对象的解构赋值要求变量名与属性名一致,但数组的话,仅要求顺序必须一致;
1 2 3 4 5 6 7 8 let arr = ["a" , "b" , "c" ];let [e, f] = arr;let a = 0 ;let b = 1 ;[a, b] = [b, a];
字符串的解构赋值 1 2 3 let str = "abc" ;let [e, f] = str
展开运算符 数组展开 1 2 3 4 5 let arr = [1 , 2 , 3 , 4 ];let arr2 = ["a" , "b" ,...arr,"c" , "d" ];let [a, b,...c] = arr;
对象展开 1 2 3 4 5 6 7 8 9 10 11 let obj = { a:1 , b:2 }; let obj2 = { ...obj, c:3 , d:4 }; let obj2 = {...obj};
set对象 set本身是一个函数,用于构建对象,还有Data、Array,调用后返回构造对象,统称构造函数,用于构造某一类型的对象,即对象的实例化。
1 2 3 4 5 6 7 8 9 let arr = [1 , 2 , 3 , 4 , 5 ]let s = new Set (arr);arr = [...s] s.size s.clear(); s.delete("a" ); s.add(6 ); s.has();
Map对象 1 2 3 4 5 6 7 8 9 10 11 12 13 let arr = [ ["a" ,1 ], ["b" ,2 ], ["c" ,3 ] ]; let s = new Map (arr);clear(); delete ();get(); has(); set(key, val);
函数新增内容 箭头函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 let fn = ()=> { console .log(1 ); }; fn(); let fn = nub => nub *2 ;let fn = (a,b...arg )=>
1 2 3 4 5 6 document ,onclick = function ( ) { let fn = ()=> { console .log(this ); }; fn(); }
使用正常函数时,this指针一般指向windows,但换成箭头函数时,this指针指向document;因为箭头函数本身没有this,调用箭头函数的this时,指向的是其声明时所在的作用域的this;
参数默认值问题:ES6中有简便方法设置参数默认值
1 2 3 let fn = (a = 2 ,b = 10 )=> { }
数组新增方法 日期对象 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 new Date ().getTime();Date .now();new Array ()Array .from();arr.forEach(); Array .from(){ let lis = document .querySelectorAll("#list li" ); let arr=[]; lis = Array .from(lis); lis.map(item => { return item; }); lis = Array .from(lis,(item,index )=> { console .log(item, index); return index; }); } { Array .of(1 ,2 ,3 ,4 ,"5" ); }
数组的find、findIndex 1 2 3 4 5 6 7 8 9 10 11 12 13 14 { let arr = ["a" , "b" , "c" , "d" ]; arr.indexOf("a" ); let arr = [1 ,2 ,3 ,4 ]; let val = arr.find((item,index )=> { if (item > 3 ){ return true ; } }); val = arr.find(item => item >= 3 ); console .log(val); }
数组扁平化处理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 let arr = { ["小明" ,"18" ], ["小刚" ,"18" ], } arr.flat(n); flat(Infinity ); let newArr = arr.flatMap((item,index )=> { cosole.log(item,index); item = item.filter((item,index )=> { return index == 0 ; }); return item; }); console .log(newArr);
flatMap参数:callback回调函数,可以生成一个新数组中元素的参数;可选参数:thisArg,执行callback函数时,使用的this值;返回值:一个包含将数组和子数组中所有元素的数组。
flatMap:只能处理一层的数组,要处理多层的话需要进行if判断是否里面还存在数组,并进行递归操作进行多次扁平化。
1 2 3 4 5 let arr = ["a" ,"b" ,"c" ,"d" ,"e" ];arr.includes("c" ,2 );
字符串新增方法 1 2 3 4 5 6 7 8 9 10 11 12 13 let str = "开课吧和面为课堂" ;str.startsWith("开课吧" ); str.endsWith("面为课堂" ); str.repeat(30 ); let p = document .querySelector("p" );let name = "小明" ;let age = 18 ;let school = "大学" ;p.innerHTML = `今年<strong>${name} </strong>就要<strong>${age} </strong>岁了,终于升入<strong>${school} </strong>了` ;
对象新增方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 let a = 0 ;let b = 1 ;let name = "小明" ;let obj = { a, b, c(){ console .log("a" ); }, [name]: 111 ; }; let obj = { a: 1 , b :2 }; let obj2 = { c : 3 , d :4 }; Object .assign(obj2 ,obj);
Babel babel是一个JavaScript编译器,用于语法编译,把JS本身不识别、不兼容的语法糖编译成兼容的,babel最简单的使用:将babel引入到页面当中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 let a = 0 ;let b = 1 ;let obj = { a, b, c(){ console .log(1 ); } }; let obj2 = { d: 4 , ...obj, e: 5 };
Promise Promise是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了Promise
对象。
所谓Promise
,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。
Promise
对象有以下两个特点:
1、对象的状态不受外界影响。Promise
对象代表一个异步操作,有三种状态:pending
(进行中)、fulfilled
(已成功)和rejected
(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是Promise
这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变;
2、一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise
对象的状态改变,只有两种可能:从pending
变为fulfilled
和从pending
变为rejected
。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。如果改变已经发生了,你再对Promise
对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。
有了Promise
对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。此外,Promise
对象提供统一的接口,使得控制异步操作更加容易。缺点:无法取消Promise
,一旦新建它就会立即执行,无法中途取消。其次,如果不设置回调函数,Promise
内部抛出的错误,不会反应到外部。第三,当处于pending
状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。
回调函数 一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
其实回调就是一种利用函数指针进行函数调用的过程.
为什么要用回调呢?比如我要写一个子模块给你用,来接收远程socket发来的命令.当我接收到命令后,需要调用你的主模块的函数, 来进行相应的处理.但是我不知道你要用哪个函数来处理这个命令,我也不知道你的主模块是什么.cpp或者.h,或者说,我根本不用关心你在主模块里怎么处理它,也不应该关心用什么函数处理它……怎么办呢?使用回调!
使用回调函数实际上就是在调用某个函数(通常是API函数)时,将自己写的一个函数(这个函数就是回调函数)的地址作为参数传递给那个函数。回调其实就是提供使用某模块的一种方法。回调函数就好比是一个中断处理函数。
在解释这种思想前我想先说明一下,回调函数固然能解决一部分系统架构问题但是绝不能再系统内到处都是,如果你发现你的系统内到处都是回调函数,那么你一定要重构你的系统。回调函数本身是一种破坏系统结构的设计思路,回调函数会绝对的变化系统的运行轨迹,执行顺序,调用顺序。回调函数的出现会让读到你的代码的人非常的懵头转向。
既然我们需要模块间的协作,同时我们又厌恶的摒弃模块间你中有我我中有你的暧昧关系那如何生成系统呢,答案是函数指针(不一定一定是函数指针)也就是使用回调的方式。如果一个对象关心另一个对象的状态变化那么给状态的变化注册回调函数让它通知你这类状态的改变,这样在封装了模块变化的同时实现了模块间的协作关系另辟独径的给对象解耦。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 var async =function (callback ) { setTimeout (function ( ) { callback('data' ); },1000 ); }; async (function (data ) { alert(data); }); var friends = ["Mike" , "Stacy" , "Andy" , "Rick" ];friends.forEach(function (eachName, index ) { console .log(index + 1 + ". " + eachName); });
需要注意的很重要的一点是回调函数并不会马上被执行。它会在包含它的函数内的某个特定时间点被“回调”(就像它的名字一样)。因此,即使第一个jQuery的例子如下所示:
1 2 3 4 5 $("#btn_1" ).click(function ( ) { alert("Btn 1 Clicked" ); });
这个回调函数在包含它的函数内的某一点执行,就好像这个回调函数是在包含它的函数中定义的一样。这意味着回调函数本质上是一个闭包。正如我们所知,闭包能够进入包含它的函数的作用域,因此回调函数能获取包含它的函数中的变量,以及全局作用域中的变量。
回调函数基本原理 命名或匿名函数作为回调 在前面的jQuery例子以及forEach的例子中,我们使用了再参数位置定义的匿名函数作为回调函数。这是在回调函数使用中的一种普遍的魔术。另一种常见的模式是定义一个命名函数并将函数名作为变量传递给函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 var allUserData = [];function logStuff (userData ) { if ( typeof userData === "string" ) { console .log(userData); } else if ( typeof userData === "object" ){ for (var item in userData){ console .log(item + ": " + userData[item]); } } } function getInput (options, callback ) { allUserData.push(options); callback(options); } getInput({name :"Rich" ,speciality :"Javascript" }, logStuff);
传递参数给回调函数 既然回调函数在执行时仅仅是一个普通函数,我们就能给它传递参数。我们能够传递任何包含它的函数的属性(或者全局书讯给)作为回调函数的参数。在前面的例子中,我们将options作为一个参数传递给了毁掉函数。现在我们传递一个全局变量和一个本地变量:
在执行之前确保回调函数是一个函数 在调用之前检查作为参数被传递的回调函数确实是一个函数,这样的做法是明智的。同时,这也是一个实现条件回调函数的最佳时间。我们来重构上面例子中的getInput函数来确保检查是恰当的。
1 2 3 4 5 6 7 8 9 function getInput (options, callback ) { allUserData.push(options); if (typeof callback === "function" ){ callback(options); } }
使用this对象的方法作为回调函数时的问题 当回调函数是一个this对象的方法时,我们必须改变执行回调函数的方法来保证this对象的上下文。否则如果回调函数被传递给一个全局函数,this对象要么指向全局window对象(在浏览器中)。要么指向包含方法的对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 var clientData = { id: 094545 , fullName : "Not Set" , setUserName: fucntion (firstName, lastName){ this .fullName = firstName + " " + lastName; } } function getUserInput (firstName, lastName, callback ) { callback(firstName, lastName); } getUserInput("Barack" ,"Obama" ,clientData.setUserName); console .log(clientData,fullName); console .log(window .fullName);
使用Call和Apply来保存this 使用回调函数时一定要注意由于函数位置的改变,导致的this指针指向位置不同
我们知道了每个Javascript中的函数都有两个方法:Call 和 Apply。这些方法被用来设置函数内部的this对象以及给此函数传递变量。
call接收的第一个参数为被用来在函数内部当做this的对象,传递给函数的参数被挨个传递(当然使用逗号分开)。Apply函数的第一个参数也是在函数内部作为this的对象,然而最后一个参数确是传递给函数的值的数组。
1 2 3 4 5 6 7 8 9 10 11 12 function getUserInput (firstName, lastName, callback. callbackObj ) { callback.apply(callbackObj, [firstName, lastName]); } getUserName("Barack" , "Obama" , clientData.setUserName, clientData); console .log(clientUser.fullName);
多重回调函数 我们可以将不止一个的回调函数作为参数传递给一个函数,就像我们能够传递不止一个变量一样。
在执行异步代码时,无论以什么顺序简单的执行代码,经常情况会变成许多层级的回调函数堆积
给你的函数命名并传递它们的名字作为回调函数,而不是主函数的参数中定义匿名函数。
模块化L将你的代码分隔到模块中,这样你就可以到处一块代码来完成特定的工作。然后你可以在你的巨型应用中导入模块
Promise基本用法 Promise
对象是一个构造函数,用来生成Promise
实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 const promise = new Promise (function (resolve, reject ) { if (){ resolve(value); } else { reject(error); } }); promise.then(function (value ) { }, function (error ) { }); let promise = new Promise (function (resolve, reject ) { console .log('Promise' ); resolve(); }); promise.then(function ( ) { console .log('resolved.' ); }); console .log('Hi!' );
下面我们写异步加载图片和实现Ajax操作的例子。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 function loadImageAsync (url ) { return new Promise (function (resolve, reject ) { const image = new Image(); image.onload = function ( ) { resolve(image); }; image.onerror = function ( ) { reject(new Error ('Could not load image at ' + url)); }; image.src = url; }); } const getJSON = function (url ) { const promise = new Promise (function (resolve, reject ) { const handler = function ( ) { if (this .readyState !== 4 ) { return ; } if (this .status === 200 ) { resolve(this .response); } else { reject(new Error (this .statusText)); } }; const client = new XMLHttpRequest(); client.open("GET" , url); client.onreadystatechange = handler; client.responseType = "json" ; client.setRequestHeader("Accept" , "application/json" ); client.send(); }); return promise; }; getJSON("/posts.json" ).then(function (json ) { console .log('Contents: ' + json); }, function (error ) { console .error('出错了' , error); }); const p1 = new Promise (function (resolve, reject ) { }); const p2 = new Promise (function (resolve, reject ) { resolve(p1); }) const p1 = new Promise (function (resolve, reject ) { setTimeout (() => reject(new Error ('fail' )), 3000 ) }) const p2 = new Promise (function (resolve, reject ) { setTimeout (() => resolve(p1), 1000 ) }) p2 .then(result => console .log(result)) .catch(error => console .log(error)) new Promise ((resolve, reject ) => { return resolve(1 ); console .log(2 ); })
Promise的各类方法使用 then方法 Promise 实例具有then
方法,也就是说,then
方法是定义在原型对象Promise.prototype
上的。它的作用是为 Promise 实例添加状态改变时的回调函数。then
方法返回的是一个新的Promise
实例(注意,不是原来那个Promise
实例)。因此可以采用链式写法,即then
方法后面再调用另一个then
方法。
第一个回调函数完成以后,会将返回结果作为参数,传入第二个回调函数。采用链式的then
,可以指定一组按照次序调用的回调函数。这时,前一个回调函数,有可能返回的还是一个Promise
对象(即有异步操作),这时后一个回调函数,就会等待该Promise
对象的状态发生变化,才会被调用。
1 2 3 4 5 6 7 8 getJSON("/post/1.json" ).then(function (post ) { return getJSON(post.commentURL); }).then(function (comments ) { console .log("resolved: " , comments); }, function (err ) { console .log("rejected: " , err); });
catch方法 Promise.prototype.catch()
方法是.then(null, rejection)
或.then(undefined, rejection)
的别名,用于指定发生错误时的回调函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 getJSON('/posts.json' ).then(function (posts ) { }).catch(function (error ) { console .log('发生错误!' , error); }); const promise = new Promise (function (resolve, reject ) { try { throw new Error ('test' ); } catch (e) { reject(e); } }); promise.catch(function (error ) { console .log(error); }); const promise = new Promise (function (resolve, reject ) { reject(new Error ('test' )); }); promise.catch(function (error ) { console .log(error); }); promise .then(function (data ) { }, function (err ) { }); promise .then(function (data ) { }) .catch(function (err ) { });
跟传统的try/catch
代码块不同的是,如果没有使用catch()
方法指定错误处理的回调函数,Promise 对象抛出的错误不会传递到外层代码,即不会有任何反应。Promise 内部的错误不会影响到 Promise 外部的代码,通俗的说法就是“Promise 会吃掉错误”。
不过,Node.js 有一个unhandledRejection
事件,专门监听未捕获的reject
错误,上面的脚本会触发这个事件的监听函数,可以在监听函数里面抛出错误。
一般总是建议,Promise 对象后面要跟catch()
方法,这样可以处理 Promise 内部发生的错误。catch()
方法返回的还是一个 Promise 对象,因此后面还可以接着调用then()
方法。
finally方法 finally()
方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。finally方法的回调函数不接受任何参数,这意味着没有办法知道,前面的 Promise 状态到底是fulfilled还是rejected。这表明,finally
方法里面的操作,应该是与状态无关的,不依赖于 Promise 的执行结果。
finally
本质上是then
方法的特例。如果不使用finally
方法,同样的语句需要为成功和失败两种情况各写一次。有了finally
方法,则只需要写一次。
finally方法的实现:
1 2 3 4 5 6 7 Promise .prototype.finally = function (callback ) { let P = this .constructor; return this .then( value => P.resolve(callback()).then(() => value), reason => P.resolve(callback()).then(() => { throw reason }) ); };
all方法 Promise.all()
方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。
1 const p = Promise .all([p1, p2, p3]);
上面代码中,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
,此时第一个被reject
的实例的返回值,会传递给p
的回调函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 const databasePromise = connectDatabase();const booksPromise = databasePromise .then(findAllBooks); const userPromise = databasePromise .then(getCurrentUser); Promise .all([ booksPromise, userPromise ]) .then(([books, user] ) => pickTopRecommendations(books, user));
上面代码中,booksPromise
和userPromise
是两个异步操作,只有等到它们的结果都返回了,才会触发pickTopRecommendations
这个回调函数。
注意,如果作为参数的 Promise 实例,自己定义了catch
方法,那么它一旦被rejected
,并不会触发Promise.all()
的catch
方法。
race方法 Promise.race()
方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。
1 const p = Promise .race([p1, p2, p3]);
上面代码中,只要p1
、p2
、p3
之中有一个实例率先改变状态,p
的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p
的回调函数。
Promise.race()
方法的参数与Promise.all()
方法一样,如果不是 Promise 实例,就会先调用下面讲到的Promise.resolve()
方法,将参数转为 Promise 实例,再进一步处理。
promise的链式调用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 function start ( ) { return new Promise ((resolve, reject ) => { resolve('start' ); }); } start() .then(data => { console .log('result of start: ' , data); return Promise .resolve(1 ); }) .then(data => { console .log('result of p1: ' , data); return Promise .reject(2 ); }) .then(data => { console .log('result of p2: ' , data); return Promise .resolve(3 ); }) .catch(ex => { console .log('ex: ' , ex); return Promise .resolve(4 ); }) .then(data => { console .log('result of p4: ' , data); });
promise俗称链式调用,它是es6中最重要的特性之一 简单的说可以不停的then调用嵌套在调用(异步之后,链式调用方式执行回调),这种操作方式称为promise
⑴. resolved(全部置为完成状态) ①.初始化:比如说以国家,省份,县市(china ,jiangshu ,xian)三个方法来演示下链式调用关系(采用setTimeout模拟异步操作)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 function china ( ) { console .log('china中国' ) var p =new Promise ( function ( resolve,reject ) { setTimeout (function ( ) { console .log('中国 国家' ) resolve('教育大省份' ) },1000 ) } ) return p; } function jiangshu (data ) { console .log('江苏' +data); var p=new Promise (function (resolve,reject ) { setTimeout (function ( ) { console .log('江苏 省份' ) resolve('地级市' ); },2000 ) }) return p; } function xian (data ) { console .log('盱眙县' +data) var p=new Promise (function (resolve,reject ) { setTimeout (function ( ) { console .log('盱眙县' ); resolve ('淮河镇' ) },2000 ) }) return p; } china ().then(jiangshu).then(xian).then(function (data ) { console .log(data) })
\ rejected(部分置为无效状态)** ①.初始化:同样的以上述的函数为例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 function china ( ) { console .log('china中国' ) var p =new Promise ( function ( resolve,reject ) { setTimeout (function ( ) { console .log('中国 国家' ) reject('教育大省份' ) },1000 ) } ) return p; } function jiangshu (data ) { console .log('江苏是' +data); varp=new Promise (function (resolve,reject ) { setTimeout (function ( ) { console .log('江苏 省份' ) resolve('地级市' ); },2000 ) }) returnp; } china() .then(jiangshu,function (data ) { console .log(data)}) china() .then(null ,function (data ) { console .log(data)}) china() .then(jiangshu).catch(function (data ) {console .log(data)})