promise的使用
栏目: JavaScript · 发布时间: 7年前
内容简介:promise的使用
本文主要介绍一下 promise
在一些场景下的使用,同时还有对 Promises/A+
规范的一点个人理解。原本是想总体的讲一下JavaScript异步编程的内容,后由于内容很多,能力有限,目前计划分成几部分。
Promise A+ 规范
为了更好的表述,我把 Promise A+
规范拆成了两部分介绍:
第一部分规范:
-
一个
Promise
的当前状态必须为以下三种状态中的一种:等待态(Pending
)、执行态(Fulfilled
)和拒绝态(Rejected
)。 -
一个
Promise
必须提供一个then
方法以访问其当前值、终值和据因。promise.then(onFulfilled, onRejected);
第二部分规范:
-
Promise then
方法需要return
一个新的Promise
出来,如下:promise2 = promise1.then(onFulfilled, onRejected);
-
如果
onFulfilled
不是函数且promise1
成功执行,promise2
必须成功执行并返回相同的值;如果onRejected
不是函数且promise1
拒绝执行,promise2
必须拒绝执行并返回相同的据因。 -
如果
promise
本身状态变更到Fulfilled
之后,返回调用onFulfilled
,onRejected
的解析值x
,与新的promise2
进行promise
的解析过程[[Resolve]](promise2, x)
,x
的取值不同,有不同的情况: -
若
x
为一个promise
,则使promise2
接受x
的状态:var promise2 = new Promise(function(resolve, reject){ resolve(); }).then(function(data){ // 对应于 x 的返回值 return new Promise(function(resolve, reject){ resolve('x = promise'); }); }); promise2.then(function(data){ // 打印 x = promise console.log(data); });
为了看的清晰一点,我把链式拆开了,正常是这样子:
new Promise(function(resolve, reject){ resolve(); }).then(function(data){ // 对应于 x 的返回值 return new Promise(function(resolve, reject){ resolve('x = promise'); }); }).then(function(data){ // 打印 x = promise console.log(data); });
-
若
x
为一个对象或者函数,如果有then
方法,将会执行then
方法,then
方法this
指向x
本身,如下:new Promise(function(resolve, reject){ resolve(); }).then(function(data){ // 对应于x的返回值 return { a: 1, then: function(resolve, reject){ // 打印 1 console.log(this.a); resolve({a: 2}); } }; }).then(function(data){ // 打印 2 console.log(data.a); });
-
如果
x
没有then
方法,那么,x
将会做为值来 满足promise2
,如下:new Promise(function(resolve, reject){ resolve(); }).then(function(data){ // 对应于x的返回值 return { a: 1 }; }).then(function(data){ // 打印 1 console.log(data.a); });
简单实现的 Promise 对象
这是一个简单实现的 Promise
对象,根据我在上面自己分出来的第一部分规范,实现了『有状态』和 then
方法。
function Promise(fn){ var state = 'pending'; var value; var deferred; function resolve(newValue){ value = newValue; state = 'resolved'; if(deferred) { handle(deferred); } } function handle(onResolved){ if(state === 'pending') { deferred = onResolved; return; } onResolved(value); } this.then = function(onResolved){ handle(onResolved); }; fn(resolve); }
当 new Promise
的时候:
- 执行
fn1
并把 resolve 方法交给fn1
准备调用 - 同步执行
then
方法,将fn2
作为参数调用handle
方法- 如果
resolve
同步执行,那么state
变更为resolved
,紧接着onResolved
也立即执行 - 如果
resolve
没有同步执行,那么state
依旧是pending
,那么将onResolved
的引用保存起来,等到resolve
异步执行的时候,再调用onResolved
方法
- 如果
new Promise(function fn1(resolve){ setTimeout(function(){ resolve('eventual value'); }, 1000); }).then(function fn2(data){ // 等待 1000ms 后,打印 eventual value console.log(data); });
接下来根据我在上面自己分出来的第二部分规范,下面的实现增加了链式和穿透的特性。
function Promise(fn){ var state = 'pending'; var value; var deferred = null; function resolve(newValue){ // 这部分是新增的,见注一 if(newValue && typeof newValue.then === 'function') { newValue.then(resolve); return; } value = newValue; state = 'resolved'; if(deferred) { handle(deferred); } } function handle(handler){ if(state === 'pending') { deferred = handler; return; } // 这部分是新增的,见注二 if(!handler.onResolved) { handler.resolve(value); return; } var ret = handler.onResolved(value); handler.resolve(ret); } this.then = function(onResolved){ return new Promise(function(resolve){ handle({ onResolved: onResolved, resolve: resolve }); }); }; fn(resolve); }
由于 then()
永远返回一个新的 Promise
对象,导致每次都至少有一个 promise
对象被创建、解决然后被忽略,这就产生了一定程度了内存浪费。
then
方法返回新的 Promise
对象,这个 promise2
要 resolve
的值是 promise1
的返回值。 handle()
函数的最后两行体现了这一点, handler
对象保存了 onResolved()
回调函数和 resolve()
函数的引用。在链式调用中保存了多个 resolve()
函数的拷贝,每一个 promise
对象的内部都拥有一个自己的 resolve()
方法,并在闭包中运行。 这建立起了第一个 promise
与第二个 promise
之间联系的桥梁。
new Promise(function(resolve){ setTimeout(function(){ resolve('promise1 eventual value'); }, 1000); }).then(function(data){ // 等待 1000ms 后,打印 promise1 eventual value console.log(data); return { then: function(resolve){ setTimeout(function(){ resolve('promise2 eventual value'); }, 1000); } } }).then(function(data){ // 等待 2000ms 后,打印 promise2 eventual value console.log(data); });
注一: then
方法内部的返回值可以是 Promise
对象, 如果是要通过 then
方法获取终值,对应第二部分规范第4和5条。
注二: new Promise.then().then(function (data) {})
里面第一个 then
会被跳过, promise
也会用上一个 promise
返回的终值来传递,对应第二部分规范第2条。
new Promise(function(resolve){ setTimeout(function(){ resolve('promise1 eventual value'); }, 1000); }).then().then(function(data){ // 等待 1000ms 后,打印 promise1 eventual value console.log(data); });
上面的代码一直忽略掉一个问题,错误处理,这里不做引述,原因是我觉得实现方式类似,需要注意的点会在下面讲。
Promise 使用
Promise.all()
当我们想使用 forEach()
的时候,我们可以使用 Promise.all()
。 Promise.all()
以一个 promise
对象组成的数组为输入,返回另一个 promise
对象。这个对象的状态只会在数组中所有的 promise
对象的状态都变为 resolved
的时候才会变成 resolved
。可以将其理解为异步的 for
循环。注意的是,如果输入的一系列 promise
对象中,有一个的状态变为 rejected
,那么 all()
返回的 promise
对象的状态也会变为 rejected
。
var array = []; for (var i = 0; i < 4; i++) { (function(){ var a = i; array.push(new Promise(function(resolve){ setTimeout(function(){ resolve(a); }, a * 1000); })); })(i); } new Promise(function(resolve){ resolve(); }).then(function(){ return Promise.all(array); }).then(function(data){ // 等待 3000ms 后, 打印 [ 0, 1, 2, 3 ] console.log(data); });
Promise.resolve()
可以将同步代码包装成 promise
形式,后面可以加 .then()
或者 .catch()
,我觉得在封装接口的时候很适用。
new Promise(function(resolve, reject){ resolve(someSynchronousValue); }).then(...);
精简为:
Promise.resolve(someSynchronousValue).then(...);
cacth() 和 then(null, …) 并不完全相同
下面两个代码片段是等价的:
somePromise().catch(function(err){ // handle error }); somePromise().then(null, function(err){ // handle error });
但是,这并不意味着下面的两个代码片段是等价的
somePromise().then(function(){ return someOtherPromise(); }).catch(function(err){ // handle error }); somePromise().then(function(){ return someOtherPromise(); }, function(err){ // handle error });
结论就是,当使用 then(resolveHandler, rejectHandler)
, rejectHandler
不会捕获在 resolveHandler
中抛出的错误。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- RecyclerView使用指南(一)—— 基本使用
- 如何使用Meteorjs使用URL参数
- 使用 defer 还是不使用 defer?
- 使用 Typescript 加强 Vuex 使用体验
- [译] 何时使用 Rust?何时使用 Go?
- UDP协议的正确使用场合(谨慎使用)
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
深度探索C++对象模型
斯坦利•B.李普曼 (Stanley B. Lippman) / 侯捷 / 电子工业出版社 / 2012-1 / 69.00元
作者Lippman参与设计了全世界第一套C++编译程序cfront,这本书就是一位伟大的C++编译程序设计者向你阐述他如何处理各种explicit(明确出现于C++程序代码中)和implicit(隐藏于程序代码背后)的C++语意。 本书专注于C++面向对象程序设计的底层机制,包括结构式语意、临时性对象的生成、封装、继承,以及虚拟——虚拟函数和虚拟继承。这本书让你知道:一旦你能够了解底层实现模......一起来看看 《深度探索C++对象模型》 这本书的介绍吧!