http://liubin.org/promises-book/
同步回调以及异步回调
回调不一定是异步过程,回调是一个函数被作为一个参数传递到另一个函数里,在那个函数执行完后再执行。
形式
1234567function f1(callback) {// f1 的代码// f1 执行完成后,调用回调函数callback();}-------------执行代码变成如下f1(f2)同步回调
12345678function f1(callback){console.log('f1')f2() //调用f2函数并执行}function f2(){console.log('f2')}f1(f2) // f1 f2异步回调
12345678910111213141516171819202122function f1(callback){setTimeout(function(){console.log('f1')callback()},2000)}function f2(){console.log('f2')}f1(f2) //f1 f2------------如果不采用回调,输出结果为f2 f1fucntion f1(){setTimeout(function(){console.log('f1')},2000)}function f2(){console.log('f2')}f1()f2()// f2 (两秒后)f1
Promise
它的思想是,每一个异步任务立刻返回一个Promise对象,由于是立刻返回,所以可以采用同步操作的流程。这个Promises对象有一个then方法,允许指定回调函数,在异步任务完成后调用。
Promise接口的基本思想是,异步任务返回一个Promise对象。
- Promise对象只有三种状态。
- 异步操作“未完成”(pending)
- 异步操作“已完成”(resolved,又称fulfilled)
- 异步操作“失败”(rejected)
- 这三种的状态的变化途径只有两种。
- 异步操作从“未完成”到“已完成”
- 异步操作从“未完成”到“失败”。
- 这种变化只能发生一次,一旦当前状态变为“已完成”或“失败”,就意味着不会再有新的状态变化了。因此,Promise对象的最终结果只有两种。
- 异步操作成功,Promise对象传回一个值,状态变为
resolved
。 - 异步操作失败,Promise对象抛出一个错误,状态变为
rejected
。
- 异步操作成功,Promise对象传回一个值,状态变为
- Promise对象使用
then
方法添加回调函数。then
方法可以接受两个回调函数,第一个是异步操作成功时(变为resolved
状态)时的回调函数,第二个是异步操作失败(变为rejected
)时的回调函数(可以省略)。一旦状态改变,就调用相应的回调函数。
then
方法可以链式使用。
|
|
上面代码中,po
的状态一旦变为resolved
,就依次调用后面每一个then
指定的回调函数,每一步都必须等到前一步完成,才会执行。最后一个then
方法的回调函数console.log
和console.error
,用法上有一点重要的区别。console.log
只显示回调函数step3
的返回值,而console.error
可以显示step1
、step2
、step3
之中任意一个发生的错误。也就是说,假定step1
操作失败,抛出一个错误,这时step2
和step3
都不会再执行了(因为它们是操作成功的回调函数,而不是操作失败的回调函数)。Promises对象开始寻找,接下来第一个操作失败时的回调函数,在上面代码中是console.error
。这就是说,Promises对象的错误有传递性。
用法
new Promise
1234567891011121314先声明生成一个Promise实例var pro = new Promise(function(resolve,reject){setTimeout(function(){console.log('2秒后done')resolve('ok','hello') //resolve这里只能接受一个参数传递出去,第二个参数没用},2000)})然后再回调pro.then(function(val){ //这里的val就是上面的第一个参数'ok'console.log(val+''+'hello')},function(){console.log('error')})//ok helloPromise.resolve(value) && Promise.reject()
12345678Promise.resolve(30)===等价于new Promise(function(resplve){resolve(30)})//value为空时,返回value为undefined的promise对象value为普通对象,返回状态为resolve的promise对象,起value为传入的参数value为promise对象,直接返回该promise对象Promise.then
Promise.then(resolved,rejected)
- resolved 和 rejected必须是函数,否则忽略
- resolved必须在 Promise 的 Resolve 状态后调用,Promise 的 value 为其第一个参数,只能被调用一次。
- rejected 必须在 Promise 的 Rejected 状态后调用,Promise 的 reason 为其第一个参数,只能被调用一次。
Promise.catch === Promise.then(undefined.rejected)
Promise.all:接收一个 promise 对象的数组作为参数,返回一个新的 promise 对象,特点如下:
- 当数组内所有 promise 对象的状态为 Resolve,其状态才为 Resolve。
- 当数组内有一个 promise 对象的状态为 Rejected,其状态就为 Rejected。
12345678910111213141516171819202122232425262728293031323334// 第一种情况var promise1 = Promise.resolve(1)var promise2 = new Promise(function (resolve, reject) {setTimeout(function () {console.log(33)resolve('yes')}, 1000);});var promise3 = Promise.all([promise1, promise2]);promise3.then(function (val) {console.log('resolve', val);}, function (e) {console.log('reject', e);});// 一秒后返回resolve [1, "yes"]传的参数是一个数组,分别为promise1和promise2的参数。且按照最后一个值的时间一起打印!// 第二种情况var promise1 = Promise.resolve(1)promise.then(function(val){console.log(val)})var promise2 = new Promise(function (resolve, reject) {setTimeout(function () {console.log(33)reject('error');}, 1000);});var promise3 = Promise.all([promise1, promise2]);promise3.then(function (val) {console.log('resolve', val);}, function (e) {console.log('reject', e);});// 11秒后打印33 reject error
#####Promise:处理异步事件
用回调获取结果(get请求,缺点:无法获取失败的结果)
12345$.get('./data.json',function(data,statusText,xhr){console.log(data)console.log(statusText)console.log(xhr) //封装后的对象}) //用jquery的get方法请求了这个路径用ajax请求(通过回调来解决异步的流程控制)
123456789101112$.ajax({url:'./data.json',method:'get',success:function(data,statusText,xhr){ //等价于上面get请求console.log(data)},error:function(xhr,statusText,reason){console.log(xhr.status) //获取到状态码console.log(statusText)console.log(reason) //请求失败}})多层回调:
123456789<body>用户:<span id="user"></span><hr>分组:<span id="group"></span><hr>其中第一个分组里面有成员:<span id="group_number"></span><script src="./node_modules/jquery/dist/jquery.js"></script><script src="./main.js"></script></body>123456789101112131415161718192021222324252627282930313233$.ajax({url:'./user.json',method:'get',success:function(data,statusText,xhr){console.log(data)$('#user').text(data.name)$.ajax({url:'./group.json',method:'get',success:function(data){console.log(data)$('#group').text([data[0].name,data[1].name].join('.'))$.ajax({url:'./group_number.json',method:'get',success:function(data){console.log(data)$('#group_number').text([data[0].name,data[1].name].join('.'))},error:function () {alert("im fine,fuck you")}})},error:function(){alert("im fine,fuck you")}})},error:function(xhr,statusText,reason){alert("im fine,fuck you")}})promise的then方法,传有两个参数,一个成功,一个失败
1234567891011121314151617181920let getuserPromise = $.get('./user.json')getuserPromise.then(function(data){console.log(data)$('#user').text(data.name)let getgroupPromise = $.get('./group.json')getgroupPromise.then(function(data){console.log(data) $('#group').text([data[0].name,data[1].name].join('.'))let getgroupnumberPromise = $.get('./group_number.json')getgroupnumberPromise.then(function(data){console.log(data)$('#group_number').text([data[0].name,data[1].name].join('.'))},function(){alert("im fine,fuck you")})},function(){alert("im fine,fuck you")})},function(){alert("im fine,fuck you")})一个简单的异步摇骰子过程
1234567891011121314151617function number(callback){setTimeout(function(){console.log('start')let random = Math.random()console.log(random)if(random>0.5){callback('big')return('big')}else{callback('small')return('small')}},1000)}let result = number(function(data){console.log(data)})return new Promise(function(resolve, reject){}) //Promise里面一定是一个函数,这个函数一定会有两个参数,resolve和reject
123456789var a = new Promise(function(resolve,reject){console.log(1)resolve()})a.then(function(){console.log(2)})console.log(3)//打印顺序:1,3,2 给promise传一个函数,会立即执行这个函数打印出1,promise回调会走完流程后再去执行,尽管它没有任何延迟立即执行成功,也会先打印出3,再去打印2以上执行顺序是:1、5、2、3、6、4、7
promise里面的函数会立即执行,但是then之后的函数会在下一次的循环中执行。
#####小结
优点:避免了层层嵌套的回调函数,也让异步操作更加容易。
缺点:一旦建立Promise就会立即执行,中途无法取消 \ 如果不设置回调函数,Promise内部抛出的错误不会反应到外部 \ 当处于Pending
状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。