JavaScript 的 Promise
先看这个 http://www.html5rocks.com/zh/tutorials/es6/promises/#toc-api 【JavaScript Promise 浏览器支持的Promise】
deferred与Promise http://joseoncode.com/2011/09/26/a-walkthrough-jquery-deferred-and-promise/ 【deferred怎么用】
以及 http://www.cnblogs.com/silin6/p/4309925.html
http://www.shakyshane.com/javascript/2013/11/16/making-promises-with-javascript/ 【deferred 与 Timeout】
还有一个PPT 很棒 http://lucifier129.github.io/nodeppt/20150528/promise.htm 【其中提到jQuery不支持的情况是1.7.2版本以及之前的版本 1.8.0之后都已经支持】
// jQuery对promise的实现叫做 deferred var ajax = $.ajax({ url: 'http://fanyi.youdao.com/openapi.do', type: 'GET', dataType: 'jsonp', data: { 'keyfrom': 'rakustrans', 'key': '1506707246', 'type': 'data', 'doctype': 'jsonp', 'version': '1.1', 'q': 'synchronize' }, }); //PS $.get对于dataType可以智能判断 对于json后缀的 可以自动将返回值转为JSON对象 无需parse() $.get('story.json').then(function(json){ console.log(json); }) //ajax本身就是一个延迟对象 也就是deferred //https://api.jquery.com/category/deferred-object/ //所以ajax可以通过.promise() 返回一个promise //ajax本身是一个thenable的对象 所以可以使用then ajax.then(function(data) { console.log(data); var rs = $.extend({},data); rs.newKey = 'newKey'; return rs; }, function(err) { console.log(err); }).then(function(data){ console.log(data); },function(err){ console.log(err); }); //和上面效果一样 //resolve的值在resolve间 ajax.promise().then(function(data) { console.log(data); var rs = $.extend({},data); rs.newKey = 'newKey'; return rs; // 注意 jQ1.7.2的then是不能传递返回值的 //从jQ 1.8 版本之后都支持返回值和promise嵌套 }, function(err) { console.log(err); }).then(function(data){ console.log(); },function(data){ console.log(); }); $.get('story.txt').then(function(data){ console.log(data); //字符串 return JSON.parse(data); //jQ1.7.2 deferred不能传递 }).then(function(data){ console.log(data); //jQ1.7.2 deferred不能传递 也就是说不论前一个then的resolve返回什么值 这里data都是第一个then的data //所以 jQ1.7.2这里得到的值是字符串 而不是parse后的对象 }); $.get('story.txt').then(function(data){ console.log(data); // return $.get('story.json'); return $.get('story.json').then(function(json){ json.newKey = 'new'; return json; }); }).then(function(rs){ console.log(rs); // jQ1.7.2 deferr不能嵌套(也就是不能在then里面再返回一个promise) 我这里jQ1.11是可以的 // rs.then(function(data){}); //报错 rs就是数据 不是一个promise //rs 就是上面嵌套的promise的resolve的值 }); //使用浏览器的Promsie (Chrome32+)========================================================================= function get(url) { // 返回一个新的 Promise return new Promise(function(resolve, reject) { // 经典 XHR 操作 var req = new XMLHttpRequest(); req.open('GET', url); req.onload = function() { // 当发生 404 等状况的时候调用此函数 // 所以先检查状态码 if (req.status == 200) { // 以响应文本为结果,完成此 Promise resolve(req.response); } else { // 否则就以状态码为结果否定掉此 Promise // (提供一个有意义的 Error 对象) reject(Error(req.statusText)); } }; // 网络异常的处理方法 req.onerror = function() { reject(Error("Network Error")); }; // 发出请求 req.send(); }); } var story = get('story.json'); story.then(function(response) { console.log("Success!", response); }, function(error) { console.error("Failed!", error); }).then(function(res) { console.log(res); //前一个若没有返回值的话这里是undefined }, function(err) { console.log(err); }); story = get('story.json'); story.then(function(res){ return JSON.parse(res); }).then(function(obj){ console.log(obj); }); //可以简写成 story.then(JSON.parse).then(function(obj) { console.log(obj); }); //这是为什么呢 虽然我们看不到JSON.parse的实现(在console中得到的结果是 function parse() { [native code] } ) //var xxx = JSON.parse(xxx) 说明这个函数接受一个参数且有返回值 所以可以猜测JSON.parse() 大致是 function(str){... retunn obj} 这个样子 //和前面的完成写法就是一样的 //再来一个setTimeout的例子 //我把ajax的例子放在前面是因为我觉得 ajax的使用场景会更容易理解 var getData = function(){ var data; setTimeout(function(){ data = 'data'; },0); return data; } console.log(getData()); //undefined //显然这样是得不到结果的 //所以我们必须传入一个回调函数 var getData = function(callback){ var data; setTimeout(function(){ data = 'data'; callback(data); },0); } getData(function(data){ console.log(data) }); //复杂了就不好办了 // 所以可以这样 var getData = function(){ var promise = new Promise(function(resolve, reject){ setTimeout(function(){ resolve('data'); },0); }); return promise; } getData().then(function(data){ console.log(data); }); //链式操作 =========================================================================================== //Promise的嵌套=======(在then的函数中又返回一个Promise)=============================================== get('story.json').then(function(data){ return get('story.txt'); }).then(function(rs){ console.log(rs); //虽然前一个Promise返回的是一个Promise对象 但是这里的rs并不是一个Promise 而是前面的Promise对象resolve的结果 //也就是story.txt中的内容 }) function getJSON(url) { return get(url).then(JSON.parse); //这里返回的是一个promise 且resolve函数的返回值是JSON.parse()后的对象 } var storyPromise; function getChapter(i) { storyPromise = getJSON('story.json'); return storyPromise.then(function(story) { //story的值是 JSON.parse(story.json) return getJSON(story.chapterUrls[i]); //和上面一样 这里虽然又返回了一个新的Promise 但实际上 }); } function getChapter2(){ // 上面一段相当于 var temp1 = get('story.json'); var temp2 = temp1.then(function(story){ return JSON.parse(story); }); var temp3 = temp2.then(function(story){ return get(story.chapterUrls[i]).then(function(data){ return JSON.parse(data); }) }) return temp3; //return 的是一个promise 其resolve值又是一个promise 这个promise的resolve值才是对应的chapter的内容 } getChapter(0).then(function(chapter) { console.log(chapter); return getChapter(1); //就是chapter-2.json }).then(function(chapter) { console.log(chapter); }); //将jQ的ajax变成标准的Promise ====================================================== var fetchData = Promise.resolve($.get('story.txt')); //转化为promise对象 //有的是cast 至少Chrome中的Promise是没有cast这个方法的 fetchData.then(function(result) { return JSON.parse(result); }).catch(function(error) { //handle error }).then(function(data){ console.log(data); }); //Promise 不会立即执行 它仍然是异步的=================================================== var promise = new Promise(function(resolve, reject) { console.log(0); resolve(1); console.log(2); }) console.log(3); promise.then(function(value) { console.log(value); }) console.log(4); //输出结果是 0 2 3 4 1 //Promise 语法糖 Promise.resolve(1); //相当于 new Promise(function(resolve) { resolve(1); }) Promise.reject(2); //相当于 new Promise(function(resolve, reject) { reject(2); }) Promise.resolve(1).catch(onRejected); //相当于 Promise.resolve(1).then(undefined, onRejected);