移动web app开发,异步代码是时常的事,好比有常见的异步操做:javascript
后面几个是CSS3 HML5加入的新API.这些接口都是会产生异步的操做前端
好比本人的一个phonegap项目,操做HTML5本地数据库(HTML5 Web Database)就是一个异步的过程,若是同时执行多个查询,势必同步代码要等待数据查询结束后调用java
/** * 初始化操做 * @return */ proto.initProcess = function(){ var self = this, prev = null , curr = null , next = null ; debug.group("start of init process"); var idx = self.chapterIndex; debug.info("PageBase: 执行初始化以前的操做!"); self.initProcessBefore(); if(idx == 0){ debug.info("PageBase: 初始化入口点从第一章开始进入"); debug.info("PageBase: 解析器解析第一章数据!"); curr = self.process(self.chapters[idx]); curr.then(function(pages){ debug.info(self.format("PageBase: 第一章数据解析完成,解析页面数为{0}" , pages.length)); self.cPages = pages; if(self.isChangeFont){ self.idx = Math.ceil((pages.length - 1) * self.idx); } self.cPages.idx = idx; ///////////////////////////////////////////////// // // 2013.1.10修改 // 若是只有一个章节的状况下 // if(1 === self.chapters.length){ deferred.all([curr]).then(self.steup.bind(self)); }else{ debug.info("PageBase:解析器解析后一章数据!"); next = self.loadNextData(idx + 1); next.then(function(args){ debug.info(self.format("PageBase: 后一章数据解析完成,解析页面数为{0}" , args.pages.length)); self.nPages = args.pages; self.nPages.idx = idx + args.index; debug.info(self.format("PageBase: 初始化数据解析完成, 当章索引{0} 当章页数{1} 下章索引{2} 下章页数{3}" , self.cPages.idx , self.cPages.length , self.nPages.idx , self.nPages.length)); debug.info("PageBase: 初始化数据解析完成,即将生成结构操做!"); }); deferred.all([curr , next]).then(self.steup.bind(self)); } }); }else if(idx == self.chapters.length -1){ debug.info("PageBase: 初始化入口点从最后一章开始进入"); debug.info("PageBase:解析器解析最后一章数据!"); prev = self.loadPrevData(idx - 1); prev.then(function(args){ self.pPages = args.pages; self.pPages.idx = args.index + 1; debug.info(self.format("PageBase: 最后一章的前一章数据解析完成,解析页面数为{0}" , args.pages.length)); curr = self.process(self.chapters[idx]); curr.then(function(pages , data){ if(self.isChangeFont){ self.idx = Math.ceil((pages.length - 1) * self.idx); } self.cPages = pages ; self.cPages.idx = idx; debug.info(self.format("PageBase: 最后一章数据解析完成,解析页面数为{0}" , pages.length)); debug.info(self.format("PageBase: 初始化数据解析完成, 前章索引{0} 前章页数{1} 当章索引{2} 当章页数{3} " , self.pPages.idx , self.pPages.length , self.cPages.idx , self.cPages.length )); debug.info("PageBase: 初始化数据解析完成,即将生成结构操做!"); }); deferred.all([prev , curr]).then(self.steup.bind(self)); }); }else{ debug.info("PageBase: 初始化入口点从中间章开始进入"); prev = self.loadPrevData(idx - 1); debug.info("PageBase:解析器解析中间章的前一章数据!"); prev.then(function(args){ self.pPages = args.pages ; self.pPages.idx = args.index; debug.info(self.format("PageBase: 中间章前一章数据解析完成,解析页面数为{0}" , args.pages.length)); debug.info("PageBase:解析器解析中间章数据!"); curr = self.process(self.chapters[idx]); curr.then(function(pages , data){ if(self.isChangeFont){ self.idx = Math.ceil((pages.length) * self.idx); // console.log("spages.length - 1",pages.length) // console.log("self.idx",self.idx) } self.cPages = pages ; self.cPages.idx = idx; debug.info(self.format("PageBase: 中间章数据解析完成,解析页面数为{0}" ,pages.length)); debug.info("PageBase:解析器解析中间章的后一章数据!"); next = self.loadNextData(idx + 1); next.then(function(args){ self.nPages = args.pages ; self.nPages.idx = idx + args.index; debug.info(self.format("PageBase: 中间章后一章数据解析完成,解析页面数为{0}" , args.pages.length)); debug.info(self.format("PageBase: 初始化数据解析完成, 前章索引{0} 前章页数{1} 当章索引{2} 当章页数{3} 下章索引{4} 下章页数{5}" , self.pPages.idx , self.pPages.length , self.cPages.idx , self.cPages.length , self.nPages.idx , self.nPages.length)); debug.info("PageBase: 初始化数据解析完成,即将生成结构操做!") }); deferred.all([prev , curr , next]).then(self.steup.bind(self)); }); }); }
可是对于异步+回调的模式,当须要对一系列异步操做进行流程控制的时候彷佛必然会面临着回调嵌套。所以怎么把异步操做“拉平”,用更好的方法去优化异步编程的体验,同时也写出更健壮的异步代码,是这两年来前端圈子里很火的话题。jquery
表明的git
本人在项目中使用 Promise/A 规范实现的 deferred-js , 比较简单轻巧.github
如何使用?web
API:ajax
var DeferredAPI = { deferred : deferred, all : all, Deferred : Deferred, DeferredList : DeferredList, wrapResult : wrapResult, wrapFailure : wrapFailure, Failure : Failure }
//Deferred对象建立 var d = new deferred.Deferred() //添加一个回调到递延的回调链 d.then(function(result) { console.log('Hello ' + result) return result }) //等待回调后触发 d.resolve('World')
每一个连接在一个回调链能够是两个函数,表明一个成功,一个失败数据库
d.then(function(result) { // 本身的代码 return result })
d.fail(function(failure) { // optionally do something useful with failure.value() return failure });
d.then(function(result) { // do something useful with the result return result }, function(failure) { // optionally do something useful with failure.value() return failure })
d.both(function(result) { // in the case of failure, result is a Failure // do something in either case return result })
请仔细对照下案例中的编程
deferred.all([prev , curr , next]).then(self.steup.bind(self));
all的方法等待全部的延时队列加载完毕后,才执行后续代码
使用起来很方便,很精简没有那么多复杂的概念
使用教程以后,下一节附源码的实现