Generator函数 基本使用 Generator函数也称之为生成器函数,可以用来生成迭代器,。也就是可以通过for…of来遍历Generator函数.并且Generator函数提供了一种异步编程的解决方案。
生成器函数和普通函数不一样,普通函数是一旦调用就会执行完毕,但是生成器函数中间可以暂停,也就是执行一会歇一会。
Generator函数的创建
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 function * go ( ) { console .log (1 ); let a = yield 'a' ; console .log (2 ); let b = yield a; console .log (3 ); return b; } let it = go ();let r1 = it.next ();console .log (r1);let r2 = it.next ('b的值' );console .log (r2);let r4 = it.next ('c的值' );console .log (r4);
如果第一次执行next方法给变量a输入值应该怎样传值?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 function * go (str ) { console .log (1 ); let a = yield str; console .log (2 ); let b = yield a; console .log (3 ); return b; } let it = go ('a的值' ); let r1 = it.next (); console .log (r1);let r2 = it.next ('b的值' );console .log (r2); let r4 = it.next ('c的值' );console .log (r4);
next方法参数 在上一个案例中,给next方法添加了相应的参数,那么该参数会 被当作上一条yield语句的返回值。
下面看一下如下程序,判断其对应的输出结果。(直接看程序)
1 2 3 4 5 6 7 8 9 function * test (num ) { let x = 3 * (yield (num + 1 )); let y = yield (x / 3 ); return (x + y + num); } let n = test (6 ); console .log (n.next ()); console .log (n.next ()); console .log (n.next ());
输出结果如下:
1 2 3 {value: 7, done: false} {value: NaN, done: false} {value: NaN, done: true}
现在将程序修改成如下的形式:
1 2 3 4 5 6 7 8 9 function * test (num ) { let x = 3 * (yield (num + 1 )); let y = yield (x / 3 ); return (x + y + num); } let n = test (6 );console .log (n.next ());console .log (n.next (3 ));console .log (n.next (3 ));
输出结果如下:
1 2 3 {value: 7, done: false} {value: 3, done: false} {value: 18, done: true}
注意:由于next方法的参数表示上一条yield语句的返回值,所以第一次使用next方法时不能带参数。
也就是第一次使用next方法时是用来启动遍历器对象的。
for…of循环 for…of循环可以自动遍历Generator函数,且此时不再需要调用next方法。
1 2 3 4 5 6 7 8 9 10 11 function * test ( ) { yield 1 ; yield 2 ; yield 3 ; yield 4 ; yield 5 ; return 6 ; } for (let v of test ()) { console .log (v); }
注意:一旦next()方法返回的对象的done属性为true, for…of循环就会终止,且不包含该返回对象,所以上面的return语句不在for…of循环中。
在前面的课程中讲过,由于JavaScript对象没有遍历的接口,无法使用for…of进行遍历,那么现在可以通过Generator函数为它加上这个接口就可以了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 let user = { name : 'zs' , age : 18 } function * test (obj ) { let keys = Reflect .ownKeys (obj); for (let key of keys) { yield [key, obj[key]]; } } for (let item of test (user)) { console .log (item); }
yield* 语句 如果在Generator函数内部调用一个Generator函数,默认情况下是没有效果的。
1 2 3 4 5 6 7 8 9 10 11 12 13 function * test ( ) { yield 'a' ; yield 'b' ; } function * test1 ( ) { yield 'x' ; test (); yield 'y' ; } for (let v of test1 ()) { console .log (v); }
要解决这个问题,需要用到 yield* 语句,用来在一个Generator函数中执行另外一个Generator函数。
上面的程序,修改成如下的形式:
1 2 3 4 5 6 7 8 9 10 11 12 13 function * test ( ) { yield 'a' ; yield 'b' ; } function * test1 ( ) { yield 'x' ; yield * test (); yield 'y' ; } for (let v of test1 ()) { console .log (v); }
其实上面的代码与下面的代码是等价的关系
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 function * test ( ) { yield 'a' ; yield 'b' ; } function * test1 ( ) { yield 'x' ; for (let v of test ()) { console .log (v); } yield 'y' ; } for (let v of test1 ()) { console .log (v); }
所以 yield* 语句等同于在Generator函数内部部署了一个for…of循环。
看一下,下面的伪代码
1 2 3 4 function * test ( ) { yield * it1; yield * it2; }
上面的代码等同于下面的代码
1 2 3 4 5 6 7 8 function * test ( ) { for (let value of it1) { yield value; } for (let value of it2) { yield value; } }
如果 yield* 后面跟着一个数组,会出现什么情况呢?
由于数组原生支持遍历器,因此会遍历数组成员。
1 2 3 4 function * test ( ) { yield *[1 , 2 , 3 , 4 , 5 , 6 ] } console .log (test ().next ())
上面的代码输出的结果为:
通过上面的输出结果可以看出,加了星号后表示返回的是数组的遍历器对象。
如果不加星号,输出结果如下:
1 {value: Array(6), done: false}
不加星号返回的是整个数组。
所以,任何数据结构只要有了Iterator接口,就可以使用yield*来进行遍历。
下面再一段程序,看一下对应的输出结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 function * test ( ) { yield 1 ; yield 2 ; return 'test' ; } function * test1 ( ) { yield 3 ; let value = yield * test (); console .log ('value=' , value); yield 4 ; } let it = test1 (); console .log (it.next ()); console .log (it.next ()); console .log (it.next ()); console .log (it.next ()); console .log (it.next ());
输出的结果如下:
1 2 3 4 5 {value: 3, done: false} {value: 1, done: false} {value: 2, done: false} value= test {value: 4, done: false} {value: undefined, done: true}
通过上面的代码可以,发现test函数中的return值,给了test1函数中的value这个变量。
关于Generator函数中的this问题 在讲解具体的this问题之前,先看一下下面的代码,是否有错误?
1 2 3 4 5 6 function * Person () { yield this .name = 'zs' ; yield this .age = 18 ; } let person = new Person ();console .log (person.name );
执行上面的代码后,发现是有错误的,因为Person既是构造函数,又是一个Generator函数,所以使用new命令就无法创建Person的对象。
怎样解决这个问题呢?
首先创建一个空对象,然后使用bind方法绑定Generator函数内部的this。这样,这个空对象就是Generator函数的实例对象了。
1 2 3 4 5 6 7 function * Person () { yield this .name = 'zs' ; yield this .age = 18 ; } let person = {} let obj = Person .bind (person)(); console .log (obj.next ());
Generator函数应用场景 状态处理 单击按钮实现图片切换,这个案例如果按照以前的做法,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 let button = document .getElementById ('btn' ) let mm = document .getElementById ('mv' ) let flag = 0 button.onclick = function ( ) { if (flag === 0 ) { mm.src = 'images/b.jpg' ; flag = 1 ; } else { mm.src = 'images/a.jpg' ; flag = 0 ; } }
使用 Generator函数处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 let button = document .getElementById ('btn' ) let mm = document .getElementById ('mv' ) let it = f (0 );button.onclick = function ( ) { it.next (); } function * f (flag ) { while (true ) { mm.src = 'images/b.jpg' ; yield flag; mm.src = 'images/a.jpg' ; yield flag; } }
使用Generator函数处理更加简单,并且更加符合函数的编程思想。(注意步骤的分析)
异步处理 前面讲过,Generator函数提供了一种异步处理的解决方案,而AJAX是典型的异步操作。
下面伪代码,直接看一下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 function * main ( ) { let result = yield request ("http://xxx.com/api" ); let resp = JSON .parse (result); console .log (resp.value ); } function request (url ) { makeAjaxCall (url, function (response ){ it.next (response); }); } let it = main ();it.next ();
注意:在makeAjaxCall函数中的next( )方法,一定要把response作为它的参数。
因为该参数会给main函数中的result变量,最终对result进行处理。
并且,上面的写法几乎与同步操作的写法完全一样,写起来非常简单。
Promise对象 ##Promise定义
回调地狱问题 在讲解具体的Promise对象的定义前,先来讲解一下回调地狱的问题。
在开发中经常使用Ajax发送请求,那么就会出现如下的情况:
1 2 3 4 5 6 7 $.ajax (url, success ( ) { $.ajax (url2, success ( ) { $.ajax (url3, success ( ) { }) }) })
以上的代码反映了,在一个Ajax的回调中,又去发送了另外一个Ajax请求,依次类推,导致了多个回调函数的嵌套,导致代码不够直观并且难以维护,这就是常说的回调地狱。
所以在实际的开发中,不希望这种不断嵌套的回调,而是希望将这种多层变成一层。
要解决这个回调地狱的问题,就要用到Promise对象。
同步模式 同步模式指的就是代码中的任务依次执行。后一个任务必须等待前一个任务结束后,才能执行。程序的执行顺序与我们代码的编写顺序是完全一致的。
异步模式 异步模式对应的API
是不会等待这个任务的结束才开始下一个任务,对于耗时操作,开启过后就立即往后执行下一个任务。
耗时任务的后续逻辑一般会通过回调函数的方式定义(例如ajax回调函数)。
Promise概念与基本使用 所谓的Promise就是一个对象,而Promise对象代表的是一个异步任务,也就是需要很长时间去执行的任务。
也就是通过Promise对象,可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数问题,也就是回调地狱的问题。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 let promise = new Promise (function (resolve, reject ) { setTimeout (function ( ) { let num = Math .random (); if (num > 0.3 ) { resolve ('成功了!' ) } else { reject ('失败了' ) } }, 3000 ) }) promise.then (function (value ) { console .log (value); }, function (reason ) { console .log (reason); })
2 使用Promise封装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 let getJSON = function (url ) { let p = new Promise (function (resolve, reject ) { let xhr = new XMLHttpRequest (); xhr.open ('GET' , url); xhr.onreadystatechange = handler; xhr.responseType = 'json' ; xhr.setRequestHeader ('Accept' , 'application/json' ); xhr.send (); function handler ( ) { if (xhr.readyState === 4 ) { if (this .status === 200 ) { resolve (this .response ) } else { reject (new Error (this .statusText )); } } } }); return p; } getJSON ('http://localhost:3005/products' ).then (function (result ) { console .log (result); }, function (error ) { console .log ('出错了:' + error) })
Promise链式调用 与传统回调函数处理异步任务相比,Promise
最大的优势就是可以实现链式调用。
这样可以最大程度的避免回调地狱的问题。
then
方法第一个参数是成功的回调,第二个参数是失败的回调,当然第二个参数是可以省略的。
then
方法最大的特点就是可以返回一个Promise
对象。
1 2 3 4 5 6 7 8 9 var promise=ajax ('/api/users.json' )var promise2=promise.then (function onFulfilled (value ){ console .log ('onFulfilled' ,value) },function onRejected (error ){ console .log ('onRejected' ,error) }) console .log (promise2)console .log (promise2===promise)
返回全新的Promise
的目的,就是为了实现一个Promise
的链条,也就是一个承诺结束后,返回一个新的承诺。每个承诺都可以负责一个异步任务,
相互之间没有什么影响,那么如果我们不断的链式调用then
方法,然后这里每个then
方法,都是为上一个then
方法返回的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 39 40 41 42 43 44 45 <!DOCTYPE html> <html lang ="en" > <head > <meta charset ="UTF-8" /> <meta http-equiv ="X-UA-Compatible" content ="IE=edge" /> <meta name ="viewport" content ="width=device-width, initial-scale=1.0" /> <title > Promise链式调用</title > </head > <body > <script > let getJson = function (url ) { let p = new Promise (function (resolve, reject ) { let xhr = new XMLHttpRequest (); xhr.open ("GET" , url); xhr.onreadystatechange = handler; xhr.responseType = "json" ; xhr.setRequestHeader ("Accept" , "application/json" ); xhr.send (); function handler ( ) { if (xhr.readyState === 4 ) { if (xhr.status == 200 ) { resolve (this .response ); } else { reject (new Error (this .statusText )); } } } }); return p; }; var promise = getJson ("http://localhost:3005/products" ); var promise2 = promise.then ( function (result ) { console .log (result); }, function (err ) { console .log ("出错了:" + err); } ); console .log ("promise2=" , promise2); console .log (promise === promise2); </script > </body > </html >
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 <!DOCTYPE html> <html lang ="en" > <head > <meta charset ="UTF-8" /> <meta http-equiv ="X-UA-Compatible" content ="IE=edge" /> <meta name ="viewport" content ="width=device-width, initial-scale=1.0" /> <title > Promise链式调用</title > </head > <body > <script > let getJson = function (url ) { let p = new Promise (function (resolve, reject ) { let xhr = new XMLHttpRequest (); xhr.open ("GET" , url); xhr.onreadystatechange = handler; xhr.responseType = "json" ; xhr.setRequestHeader ("Accept" , "application/json" ); xhr.send (); function handler ( ) { if (xhr.readyState === 4 ) { if (xhr.status == 200 ) { resolve (this .response ); } else { reject (new Error (this .statusText )); } } } }); return p; }; getJson ("http://localhost:3005/products" ) .then (function (value ) { console .log (value); console .log ("111" ); return getJson ("http://localhost:3005/cart" ); }) .then (function (value ) { console .log ("then2=" , value); console .log ("222" ); }) .then (function (value ) { console .log (value); console .log ("333" ); }) .then (function (value ) { console .log ("444" ); return "abc" ; }) .then (function (value ) { console .log ("555555" ); console .log ("then5=" , value); }); </script > </body > </html >
Promise
异常处理如果Promise
执行结果失败,会调用我们所为其添加的onRejected
回调函数。
1 2 3 4 5 6 var promise=ajax ('/api/users.json' )var promise2=promise.then (function onFulfilled (value ){ console .log ('onFulfilled' ,value) },function onRejected (error ){ console .log ('onRejected' ,error) })
例如,我们请求了不存在地址,或者是我们在ajax
方法内部出现了异常(throw new Error()
),都会执行onRejected
函数。
所以说onRejected
就是处理Promise
中的异常。当然关于异常处理,我们还有另外一种用户就是使用Promise
对象的catch
方法来完成。
下面,我们来实现以下
1 2 3 4 5 ajax ('/api/users.json' ).then (function onFulfilled (value ){ console .log ('onFulfilled' ,value) }).catch (function onRejected (error ){ console .log ('onRejected' ,error) })
在上面的代码中,使用then
注册了成功的回调,使用catch
来处理异常。
其实这个catch
方法就是then
方法的别名。
Promise并行执行 例如,一个页面中有可能会与遇到多个请求服务端接口的情况,而这些请求之间没有相互的依赖关系。
那最好的选择就是同时请求服务端,避免一个一个的请求,而消耗过多的时间。
当然,你可能会说,这个实现起来非常的简单啊,把我们前面所写的ajax
函数,多调用几次就可以了,如下所示:
1 2 ajax ('/api/users.json' )ajax ('/api/posts.json' )
但是问题是,我们怎么知道所有的请求都结束了呢?
当然,你可能会说,我们定义一个计数器,每个请求结束后,让这个计数器累加一下,当累加的个数,与我们的任务数相同后,就表示所有的任务结束了。
这种方式比较麻烦。为了解决这个问题,Promise
中提供了一个all
方法。该方法接收的是一个数组,数组中的每个元素都是一个Promise
对象。我们可以把这些Promise
对象,看作是一个一个的异步任务。all
方法会返回一个全新的Promise
对象。当all
方法内部所有的Promise
对象都执行完毕后,这时我们才会获取到all
方法所返回的新的Promise
对象。该Promise
对象获取到的结果是一个数组。在这个数组中包含了每个异步任务执行的结果。
需要注意的就是all
方法中所有Promise
对象都执行成功了,才表示成功,只要有一个失败了,那么all
方法的执行就失败了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 let promise1 = new Promise (function (resolve, reject ) { setTimeout (function ( ) { let num = Math .random (); if (num > 0.3 ) { resolve ('成功了!' ) } else { reject ('失败了1' ) } }, 3000 ) }) let promise2 = new Promise (function (resolve, reject ) { setTimeout (function ( ) { let num = Math .random (); if (num > 0.3 ) { resolve ('成功了!' ) } else { reject ('失败了2' ) } }, 3000 ) }) Promise .all ([promise1, promise2]).then (function (data ) { console .log (data); })
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 <!DOCTYPE html> <html lang ="en" > <head > <meta charset ="UTF-8" /> <meta http-equiv ="X-UA-Compatible" content ="IE=edge" /> <meta name ="viewport" content ="width=device-width, initial-scale=1.0" /> <title > Promise并行执行</title > </head > <body > <script > let getJson = function (url ) { let p = new Promise (function (resolve, reject ) { let xhr = new XMLHttpRequest (); xhr.open ("GET" , url); xhr.onreadystatechange = handler; xhr.responseType = "json" ; xhr.setRequestHeader ("Accept" , "application/json" ); xhr.send (); function handler ( ) { if (xhr.readyState === 4 ) { if (xhr.status == 200 ) { resolve (this .response ); } else { reject (new Error (this .statusText )); } } } }); return p; }; Promise .all ([ getJson ("http://localhost:3005/products" ), getJson ("http://localhost:3005/cart" ), getJson ("http://localhost:3005/ddd" ).catch (() => {}), ]) .then ((response ) => { console .log (response); }) .catch ((err ) => { console .log (err); }); </script > </body > </html >
Promise.race( ) 与all()
方法的区别是:
Promise.all( )
是等待所有任务结束后才会结束。
‘Promise.race( )’只要有一个任务完成就结束。
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 <!DOCTYPE html> <html lang ="en" > <head > <meta charset ="UTF-8" /> <meta http-equiv ="X-UA-Compatible" content ="IE=edge" /> <meta name ="viewport" content ="width=device-width, initial-scale=1.0" /> <title > Promise中race方法</title > </head > <body > <script > let promise1 = new Promise (function (resolve, reject ) { setTimeout (function ( ) { let num = Math .random (); if (num > 0.3 ) { resolve ("成功了1!" ); } else { reject ("失败了1" ); } }, 3000 ); }); let promise2 = new Promise (function (resolve, reject ) { setTimeout (function ( ) { let num = Math .random (); if (num > 0.3 ) { resolve ("成功了2!" ); } else { reject ("失败了2" ); } }, 3000 ); }); let p = Promise .race ([promise1, promise2]) .then (function (data ) { console .log (data); }) .catch ((err ) => { console .log (err); }); </script > </body > </html >
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 <!DOCTYPE html> <html lang ="en" > <head > <meta charset ="UTF-8" /> <meta http-equiv ="X-UA-Compatible" content ="IE=edge" /> <meta name ="viewport" content ="width=device-width, initial-scale=1.0" /> <title > Promise中race方法</title > </head > <body > <script > let getJson = function (url ) { let p = new Promise (function (resolve, reject ) { let xhr = new XMLHttpRequest (); xhr.open ("GET" , url); xhr.onreadystatechange = handler; xhr.responseType = "json" ; xhr.setRequestHeader ("Accept" , "application/json" ); xhr.send (); function handler ( ) { if (xhr.readyState === 4 ) { if (xhr.status == 200 ) { resolve (this .response ); } else { reject (new Error (this .statusText )); } } } }); return p; }; var promise = getJson ("http://localhost:3005/products" ); const timeout = new Promise (function (resolve, reject ) { setTimeout (() => reject (new Error ("timeout" )), 100 ); }); Promise .race ([promise, timeout]) .then ((value ) => { console .log (value); }) .catch ((error ) => { console .log (error); }); </script > </body > </html >
Promise
静态方法在Promise
中还有几个静态方法也会使用到。
第一个 是Promise.resolve()
其作用就是将一个值,快速的转换成Promise
对象。
1 2 3 Promise .resolve ('foo' ).then (function (value ){ cosnole.log (value) })
第二个为Promise.reject()
方法,该方法创建一个失败的Promise
对象。
1 2 3 Promise .reject (new Error ('rejected' )).catch (function (error ){ console .log (error) })
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 <!DOCTYPE html> <html lang ="en" > <head > <meta charset ="UTF-8" /> <meta http-equiv ="X-UA-Compatible" content ="IE=edge" /> <meta name ="viewport" content ="width=device-width, initial-scale=1.0" /> <title > Promise执行顺序问题</title > </head > <body > <script > console .log ("start" ); setTimeout (() => { console .log ("setTimeout" ); }, 0 ); Promise .resolve () .then (() => { console .log ("promise" ); }) .then (() => { console .log ("promise2" ); }) .then (() => { console .log ("promise3" ); }); console .log ("end" ); </script > </body > </html >
模拟Promise对象 搭建基本结构 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <script> function MyPromise (task ){ let that = this ; that.status ='Pending' ; function resolve ( ){ } function reject ( ){ } task (resolve,reject); } let myPromise =new MyPromise (function (resolve,reject ){ }) </script>
###异常处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <script> function MyPromise (task ) { let that = this ; that.status = "Pending" ; function resolve ( ) {} function reject ( ) { if (that.status === "Pending" ) { that.status = "Rejected" ; } } try { task (resolve, reject); } catch (e) { reject (e); } } MyPromise .prototype .then = function (onFulfilled, onRejected ) {}; let myPromise = new MyPromise (function (resolve, reject ) {}); </script>
then方法处理与基本测试 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 <!DOCTYPE html> <html lang ="en" > <head > <meta charset ="UTF-8" /> <meta http-equiv ="X-UA-Compatible" content ="IE=edge" /> <meta name ="viewport" content ="width=device-width, initial-scale=1.0" /> <title > 构建自己的Promise对象</title > </head > <body > <script > function MyPromise (task ) { let that = this ; that.status = "Pending" ; that.value = undefined ; that.onResolvedCallbacks = []; that.onRejectedCallbacks = []; function resolve (value ) { if (that.status === "Pending" ) { that.status = "Resolved" ; that.value = value; that.onResolvedCallbacks .forEach ((item ) => item (that.value )); } } function reject (reason ) { if (that.status === "Pending" ) { that.status = "Rejected" ; that.value = reason; that.onRejectedCallbacks .forEach ((item ) => item (that.value )); } } try { task (resolve, reject); } catch (e) { reject (e); } } MyPromise .prototype .then = function (onFulfilled, onRejected ) { let that = this ; that.onResolvedCallbacks .push (onFulfilled); that.onRejectedCallbacks .push (onRejected); }; let myPromise = new MyPromise (function (resolve, reject ) { setTimeout (function ( ) { let num = Math .random (); if (num > 0.3 ) { resolve ("成功了" ); } else { reject ("失败了" ); } }, 3000 ); }); myPromise.then ( function (value ) { console .log (value); }, function (reason ) { console .log (reason); } ); </script > </body > </html >
完善操作 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 <!DOCTYPE html> <html lang ="en" > <head > <meta charset ="UTF-8" /> <meta http-equiv ="X-UA-Compatible" content ="IE=edge" /> <meta name ="viewport" content ="width=device-width, initial-scale=1.0" /> <title > 构建自己的Promise对象</title > </head > <body > <script > function MyPromise (task ) { let that = this ; that.status = "Pending" ; that.value = undefined ; that.onResolvedCallbacks = []; that.onRejectedCallbacks = []; function resolve (value ) { if (that.status === "Pending" ) { that.status = "Resolved" ; that.value = value; that.onResolvedCallbacks .forEach ((item ) => item (that.value )); } } function reject (reason ) { if (that.status === "Pending" ) { that.status = "Rejected" ; that.value = reason; that.onRejectedCallbacks .forEach ((item ) => item (that.value )); } } try { task (resolve, reject); } catch (e) { reject (e); } } MyPromise .prototype .then = function (onFulfilled, onRejected ) { let that = this ; if (that.status === "Resolved" ) { onFulfilled (that.value ); } if (that.status === "Rejected" ) { onRejected (that.value ); } that.onResolvedCallbacks .push (onFulfilled); that.onRejectedCallbacks .push (onRejected); }; let myPromise = new MyPromise (function (resolve, reject ) { resolve ("成功了" ); }); myPromise.then ( function (value ) { console .log (value); }, function (reason ) { console .log (reason); } ); </script > </body > </html >
async函数 常见异步编程方式 回调函数 JavaScript 语言对异步编程的实现,就是回调函数。所谓回调函数,就是把任务的第二段单独写在一个函数里面,等到重新执行这个任务的时候,就直接调用这个函数。 它的英语名字 callback,直译过来就是”重新调用”。
1 2 3 4 fs.readFile ('/etc/passwd' , function (err, data ) { if (err) throw err; console .log (data); });
上面代码中,readFile 函数的第二个参数,就是回调函数,也就是任务的第二段。等到操作系统返回了 /etc/passwd 这个文件以后,回调函数才会执行。
####Promise对象
回调函数本身并没有问题,它的问题出现在多个回调函数嵌套。假定读取A文件之后,再读取B文件,代码如下。
1 2 3 4 5 fs.readFile (fileA, function (err, data ) { fs.readFile (fileB, function (err, data ) { }); });
不难想象,如果依次读取多个文件,就会出现多重嵌套 .这样就产生了,我们前面讲解的回调地狱问题。
而Promise对象就是为了解决这个问题。
1 2 3 4 5 6 7 8 9 10 11 12 13 readFile (fileA).then (function (data ){ console .log (data.toString ()); }) .then (function ( ){ return readFile (fileB); }) .then (function (data ){ console .log (data.toString ()); }) .catch (function (err ) { console .log (err); });
通过Promise解决了回调地狱的问题。
Generator函数 Generator函数,就是一个封装的异步任务,或者说是异步任务的容器。异步操作需要暂停的地方,都用到yield语句。
下面的案例是前面用Generator函数封装的AJAX的异步操作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 function * main ( ) { let result = yield request ("http://xxx.com/api" ); let resp = JSON .parse (result); console .log (resp.value ); } function request (url ) { makeAjaxCall (url, function (response ){ it.next (response); }); } let it = main ();it.next ();
async函数 基本用法 1 2 3 4 5 async function test ( ) { let result = await Math .random (); console .log (result); } test ();
async: 表示函数中有异步操作,await 必须出现在 async 函数内部,不能单独使用。
await: 表示紧跟在后面的表达式需要等待结果。一般情况下await后面跟的是一个耗时的操作或者一个异步的操作。
使用方式 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 <script> function sleep (second ) { return new Promise (function (resolve, reject ) { setTimeout (function ( ) { let num = Math .random (); if (num > 0.8 ) { resolve ("成功了" ); } else { reject ("失败了" ); } }, second); }); } async function awaitDemo ( ) { let result = await sleep (3000 ); return result; } awaitDemo () .then (function (data ) { console .log ("data=" , data); }) .catch (function (err ) { console .log ("error=" , err); }); console .log ("执行其它的代码" ); </script>
处理异步请求 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 <script> let getJson = function (url ) { let p = new Promise (function (resolve, reject ) { let xhr = new XMLHttpRequest (); xhr.open ("GET" , url); xhr.onreadystatechange = handler; xhr.responseType = "json" ; xhr.setRequestHeader ("Accept" , "application/json" ); xhr.send (); function handler ( ) { if (xhr.readyState === 4 ) { if (xhr.status == 200 ) { resolve (this .response ); } else { reject (new Error (this .statusText )); } } } }); return p; }; async function getAjax ( ) { try { let result = await getJson ("http://localhost:3005/products" ); console .log (result); } catch (err) { console .log (err); } } getAjax (); </script>
请求依赖关系的处理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <script> function sleep (second, param ) { return new Promise ((resolve, reject ) => { setTimeout (() => { resolve (param); }, second); }); } async function test ( ) { let result1 = await sleep (2000 , "req01" ); let result2 = await sleep (1000 , "req02" + result1); let result3 = await sleep (500 , "req03" + result2); console .log (result1, result2, result3); } test (); </script>
并且处理的问题 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 <script> let getJSON = function (url ) { let p = new Promise (function (resolve, reject ) { let xhr = new XMLHttpRequest (); xhr.open ("GET" , url); xhr.onreadystatechange = handler; xhr.responseType = "json" ; xhr.setRequestHeader ("Accept" , "application/json" ); xhr.send (); function handler ( ) { if (xhr.readyState === 4 ) { if (this .status === 200 ) { resolve (this .response ); } else { reject (new Error (this .statusText )); } } } }); return p; }; async function getAJAX ( ) { try { let result = getJSON ("http://localhost:3005/products" ); let result1 = getJSON ("http://localhost:3005/products" ); let result2 = getJSON ("http://localhost:3005/products" ); let p = await Promise .all ([result, result1, result2]); console .log (p); console .log ("clear the loading~" ); } catch (e) { console .log (e); } } getAJAX (); </script>