Bare bones Promises/A+ implementation 简单的 Promises/A+ 实现
- MiniPromise.resolve()
- MiniPromise.reject()
- MiniPromise.then()
- MiniPromise.all()
- MiniPromise.race()
- MiniPromise.any()
- MiniPromise.allSettled()
通篇讲解promise,学习promise相关内容,熟练使用promise;了解promise可以做什么,不可以做什么。
promise是抽象异步处理对象以及对其进行各种操作的组件。promise最初被提出是在E语言中,它是基于并列/并行处理设计的一编程语言。
我们在 基于JavaScript的异步处理,大都想到使用回调函数。
使用回调函数的异步处理
getAsync("file.txt",function(error,result){// 取得失败时的处理if(error){throwerror;}// 取得成功时的处理});node.js 规定在 JavaScript 的回调函数的一个参数为 error 对象,这是 Node.js 的一般写法。
promsie 时可以将复杂的异步处理轻松地进行模式化,写法:
varpromise=getAsyncPromise('file.txt');promise.then(function(result){// 获取文件内容成功时的处理}).catch(function(error){// 获取文件内容失败时的处理});Promise类似于 XMLHttpRequest,从构造函数 Promise 来创建一个新建的 promise 对象作为接口。
要想创建一个 promise 对象,可以使用 new 来调用 promise 的构造器来进行实例化。
varpromise=newPromise(function(resolve,reject){// 异步处理// 处理结束后,调用 resolve 或 reject});通过 new 生成的 promise 对象为了设置其值在 resolve (成功)/ reject(失败) 时调用的回调函数可以使用 promise.then() 实例方法。
promise.then(onFulfilled,onRejected)- resolve(成功)时
- onFulfilled 会被调用
- reject(失败)时
- onRejected 会被调用
onFulfilled, onRejected 两个都是可选参数。
promise.then 成功和失败时都可以使用。对于异常进行处理时可以采用 promise.then(undefined, onRejected) 这种方式,只指定 reject 时的回调函数即可。不过这种情况下请使用 promise.catch(onRejected) 是更好的选择。
promise.catch(onRejected)像 promise 这样的全局对象还拥有一些静态方法。
包括 Promise.all() 还有 Promise.resolve() 等在内,主要都是一些对 Promise 进行操作的辅助方法。
promise工作流程代码:
functionasyncFunction(){returnnewPromise(function(resolve,reject){setTimeout(function(){resolve('Async Hello world');},1000);});}new Promise 构造器之后,会返回一个 promise 对象
asyncFunction().then(function(value){console.log(value);// => 'Async Hello world'}).catch(function(error){console.log(error);});为promise对象设置 .then 调用返回值时的回调函数。
asyncFunction 这个函数会返回 promise 对象,对于这个 promise 对象,我们调用它的 then 方法来设置 resolve 后的回调函数, catch 方法来设置发生错误时的回调函数。
该promise对象会在setTimeout之后的 1000ms 被 resolve, 这时 then 的调用函数会被调用,并输出 'Async Hello world'。
在这种情况下 catch 的回调函数并不会被执行(因为promise返回了resolve),不过如果运行环境没有提供 setTimeout 函数的话,那么就会产生异常,在 catch 中设置的回调函数就会被执行。
如果不使用catch方法,只使用then方法的话:
只声明 promise.then(onFulfilled, onRejected)
asyncFunction().then(function(value){console.log(value);},function(error){console.log(error);});用 new Promise 实例化的 promise 对象有三种状态:
- has-resolution: FulFilled
- resolve(成功)时,此时调用 onFulfilled
- has-rejection: Rejected
- reject(失败)时,此时调用 onRejected
- unresolved: Pending
- 既不是resolve也不是reject的状态。也就是promise对象刚被创建后的初始化状态等
建议使用:Pending,Fulfilled,Rejected 的状态名称进行讲述。
pending -> value -> Fulfilled
pending -> error -> Rejected
promise 对象的状态,从 Pending 转换为 Fulfilled 或 Rejected 之后,这个 promise 对象的状态就不会再发生任何变化。
在 .then 后执行的函数可以肯定地说只会被调用一次。并且 Fulfilled 和 Rejected 这两个中的任一状态都可以表示为 Settled(不变的)。
resolve(成功) 或 reject(失败)
当 promise 的对象状态发生变化时,用 .then 来定义只会被调用一次的函数。
创建promise对象的流程:
- new Promise(fn) 返回一个 promise 对象
- 在 fn 中指定异步等处理
- 处理结果正常的话,调用 resolve(处理结果值)
- 处理结果错误的话,调用 reject(Error对象)
用 Promise 来通过异步处理方式来获取 XMLHttpRequest(XHR)的数据:
来创建XHR的promise对象
// 运行示例varURL="http://xxx.org/get";getURL(URL).then(functiononFulfilled(value){console.log(value);}).catch(functiononRejected(error){console.error(error);});functiongetURL(URL){returnnewPromise(function(resolve,reject){varreq=newXMLHttpRequest();req.open('GET',URL,true);req.onload=function(){if(req.status===200){resolve(req.responseText);}else{reject(newError(req.statusText));}};req.onerror=function(){reject(newError(req.statusText));};req.send();});}为promise对象添加处理方法主要有以下两种
- promise对象被 resolve 时的处理(onFulfilled)
- promise对象被 reject 时的处理(onRejected)
此时所谓的 通信成功 , 指的就是在被resolve后, promise对象变为FulFilled状态 。
被resolve后的处理,可以在 .then 方法中传入想要调用的函数。
用 new Promise 方法创建promise对象
用 .then 或 .catch 添加promise对象的处理函数
静态方法 Promise.resolve(value) 可以认为是 new Promise() 方法的快捷方式。
Promise.resolve(18); 等同于 可以认为是以下代码的语法糖:
newPromise(function(resolve){resolve(18);});方法 Promise.resolve(value); 的返回值也是一个promise对象
Promise.resolve(18).then(function(value){console.log(value);})Promise.resolve 方法另一个作用就是将 thenable 对象转换为promise对象。
Promise.resolve 方法另一个作用就是将 thenable 对象转换为promise对象。
ES6 Promises里提到了Thenable这个概念,简单来说它就是一个非常类似promise的东西。
就像我们有时称具有 .length 方法的非数组对象为Array like一样,thenable指的是一个具有 .then 方法的对象。
这种将thenable对象转换为promise对象的机制要求thenable对象所拥有的 then 方法应该和Promise所拥有的 then 方法具有同样的功能和处理过程,在将thenable对象转换为promise对象的时候,还会巧妙的利用thenable对象原来具有的 then 方法。
到底什么样的对象能算是thenable的呢,最简单的例子就是 jQuery.ajax(),它的返回值就是thenable的。
$.ajax('/json/comment.json');// => 拥有 `.then` 方法的对象这个thenable的对象可以使用 Promise.resolve 来转换为一个promise对象。
将thenable对象转换promise对象
varpromise=Promise.resolve($.ajax('/json/comment.json'));// => promise对象promise.then(function(value){console.log(value);});Promise.resolve 方法可以认为它的作用就是将传递给它的参数填充(Fulfilled)到promise对象后并返回这个promise对象。
Promise.reject(error) 是 new Promise() 方法的快捷方式。
比如 Promise.reject(new Error("出错了")) 就是下面代码的语法糖形式。
newPromise(function(resolve,reject){reject(newError("出错了"));});这段代码的功能是调用该promise对象通过then指定的 onRejected 函数,并将错误(Error)对象传递给这个 onRejected 函数。
Promise.reject(newError("BOOM!")).catch(function(error){console.error(error);});varpromise=newPromise(function(resolve){console.log("inner promise");// 1resolve(18);});promise.then(function(value){console.log(value);// 3});console.log("outer promise");// 2functiononReady(fn){varreadyState=document.readyState;if(readyState==='interactive'||readySate==='complete'){fn();}else{window.addEventListener('DOMContentLoaded',fn);}}onReady(function(){console.log('DOM fully loaded and parsed');});promise可以写成方法链的形式
aPromise.then(functiontaskA(value){// task A}).then(functiontaskB(vaue){// task B}).catch(functiononRejected(error){console.log(error);});promise chain
functiontaskA(){console.log("Task A");}functiontaskB(){console.log("Task B");}functiononRejected(error){console.log("Catch Error: A or B",error);}functionfinalTask(){console.log("Final Task");}varpromise=Promise.resolve();promise.then(taskA).then(taskB).catch(onRejected).then(finalTask);- then
- 注册 onFulfilled 时的回调函数
- catch
- 注册 onRejected 时的回调函数
return 返回值
每个方法中 return 的值不仅只局限于字符串或者数值类型,也可以是对象或者promise对象等复杂类型
return的值会由 Promise.resolve(return的返回值); 进行相应的包装处理,因此不管回调函数中会返回一个什么样的值,最终 then 的结果都是返回一个新创建的promise对象
Promise.then 不仅仅是注册一个回调函数那么简单,它还会将回调函数的返回值进行转换,创建并返回一个 promise 对象。
Promise.cath 只是 promise.then(undefined, onRejected); 方法的一个别名而已。这个方法用来注册当promise对象状态变为Rejected时的回调函数。
varpromise=Promise.reject(newError("message"));promise.catch(function(error){console.error(error);});varpromise=Promise.reject(newError("message"));promise["catch"](function(error){console.error(error);});varpromise=Promise.reject(newError("message"));promise.then(undefined,function(error){console.error(error);});不管是 then 还是 catch 方法调用,都返回了一个新的promise对象。
varaPromise=newPromise(function(resolve){resolve(100);});varthenPromise=aPromise.then(function(value){console.log(value);});varcatchPromise=thenPromise.catch(function(error){console.error(error);});console.log(aPromise!==thenPromise);// => trueconsole.log(thenPromise!==catchPromise);// => truethen 和 catch 都返回了和调用者不同的promise对象。
promise object -> then(promise object)(value -> onFulfilled; error -> onRejected) -> catch(error -> onRejected)
Promise.all 接收一个 promise对象的数组作为参数,当这个数组里的所有promise对象全部变为resolve或reject状态的时候,它才会去调用 .then 方法。
使用一个计时器来计算一下程序执行时间
// `delay`毫秒后执行resolvefunctiontimerPromisefy(delay){returnnewPromise(function(resolve){setTimeout(function(){resolve(delay);},delay);});}varstartDate=Date.now();// 所有promise变为resolve后程序退出Promise.all([timerPromisefy(1),timerPromisefy(32),timerPromisefy(64),timerPromisefy(128)]).then(function(values){console.log(Date.now()-startDate+'ms');// 約128msconsole.log(values);// [1,32,64,128]});Promise.race 方法 ,接收一个promise对象数组为参数。
Promise.all 在接收到的所有的对象promise都变为 FulFilled 或者 Rejected 状态之后才会继续进行后面的处理, 与之相对的是 Promise.race 只要有一个promise对象进入FulFilled 或者 Rejected 状态的话,就会继续进行后面的处理。
- 使用 promise.then(onFulfilled, onRejected) 的话
- 在 onFulfilled 中发生异常的话,在 onRejected 中是捕获不到这个异常的。
- 在 promise.then(onFulfilled).catch(onRejected) 的情况下
- then 中产生的异常能在 .catch 中捕获
- .then 和 .catch 在本质上是没有区别的
- 需要分场合使用。
由于 .catch 方法是 .then 的别名,我们使用 .then 也能完成同样的工作。只不过使用 .catch 的话意图更明确,更容易理解
- promise.then(onFulfilled, onRejected);
- promise.catch(onRejected);
Promise.resolve(promise);
Promise.resolve(thenable);
Promise.resolve(object);
- 接收到promise对象参数的时候
- 返回的还是接收到的promise对象
- 接收到thenable类型的对象的时候
- 返回一个新的promise对象,这个对象具有一个 then 方法
- 接收的参数为其他类型的时候(包括JavaScript对或null等)
- 返回一个将该对象作为值的新promise对象
- Promise.reject(object)
和 Promise.resolve不同的是,即使Promise.reject接收到的参数是一个promise对象,该函数也还是会返回一个全新的promise对象。
- Promise.all(promiseArray);
生成并返回一个新的promise对象。
参数传递promise数组中所有的promise对象都变为resolve的时候,该方法才会返回, 新创建的promise则会使用这些promise的值。
如果参数中的任何一个promise为reject的话,则整个Promise.all调用会立即终止,并返回一个reject的新的promise对象。
由于参数数组中的每个元素都是由 Promise.resolve 包装(wrap)的,所以Paomise.all可以处理不同类型的promose对象。
- Promise.race(promiseArray)
生成并返回一个新的promise对象。
参数 promise 数组中的任何一个promise对象如果变为resolve或者reject的话, 该函数就会返回,并使用这个promise对象的值进行resolve或者reject。