前言
从事前端的朋友或多或少的接触过Promise,当代码中回调函数层级过多你就会发现Promise异步编程的魅力,相信此文一定能帮你排忧解惑!
Promise概念
Promise是JS异步编程中的重要概念,异步抽象处理对象,是目前比较流行Javascript异步编程解决方案之一
或许是笔者理解能力有限,对官方术语怎么也感受不到亲切,下面我来用通俗易懂的语言解释下:
Promise是一个包含三种状态的对象(pending、fulfilled、rejected),可以链式的处理异步请求(then方法)并能很好地处理异常问题,是解决回调地狱的良好方案之一。
回调函数处理多层异步示例
$.ajax({ url: url1, success: function(rsp){ $.ajax({ url: url2, success: function(rsp){ $.ajax({ url: url3, success: function(rsp){ //do sth }, error: function(error){ } }); }, error: function(error){ } }); }, error: function(error){ } });
将promise封装在$.ajax中
$.ajax = function(config){ return new Promise(function(resolve, reject){ //1省略... xmlhttp.onreadystatechange = function(){ if(xmlhttp.status==200){ resolve(rspData); }else{ reject(xmlhttp.statusText); } }; //2省略... }) } $.ajax({url: url1}).then(function(val){ return $.ajax({url: val.url}) }).then(function(val){ return $.ajax({url: val.url}) }).catch(function(err){ console.log(err); }}
封装好的Promise处理异步可读性可维护性以及代码美观度不言而喻
Promise API
'new' Promise
//pending状态的promise var promise = new Promise(function(resolve, reject){ //do sth }) //fulfilled状态的promise var promise = Promise.resolve(1).then(function resolve(value){console.log(value)}); // var promise = new Promise(function(resolve){resolve(1)}) //rejected状态的promise var promise = Promise.reject(new Error('error')).catch(function(error){console.error(error)}); // var promise = new Promise(function(resolve,reject){resolve(new Error('error'))})
Promise.prototype.then
Promise#then
promise.then(onFulfilled, onRejected)
返回一个新的promise。这里经常会有一个疑问:为什么不返回原来的promise,个人是这样认为的,若返回同一个promise则状态不一致,promise规范说明当pending至fulfilled/rejected时状态确定后不能再改变。
Promise.prototype.catch
Promise#catch promise.catch(function(error){ throw new Error(error); })
注意:IE8及以下版本会出现 identifier not found 的语法错误,可将点标记法改为中括号标记法
promise['catch'](function(error){ throw new Error(error); })
rejected状态的promise抛出异常
相当于
promise.then(undefined, onRejected)
then & catch 结合示例
promise.then(function f1(value){ //do sth 1 }).then(function f2(value){ //do sth 2 }).then(function f3(value){ //do sth 3 }).catch(function(error){ console.log(error); })
Promise.prototype.finally
promise.finally(onFinally)
返回一个Promise,在promise执行结束时,无论结果是fulfilled或者是rejected,在执行then()和catch()后,都会执行
Promise.all
promise.all([promise1, promise2, promise3]).then(resolve);
示例
// `delay`毫秒后执行resolve function timerPromisefy(delay) { return new Promise(function (resolve) { setTimeout(function () { resolve(delay); }, delay); }); } var startDate = Date.now(); // 所有promise变为resolve后程序退出 Promise.all([ timerPromisefy(1), timerPromisefy(32), timerPromisefy(64), timerPromisefy(128) ]).then(function (values) { console.log(Date.now() - startDate + 'ms'); // 约128ms console.log(values); // [1,32,64,128] });
在接收到所有的对象promise都变为 FulFilled 返回一个resolve(array),或者 某一个promise对象变成Rejected 状态返回resolve(err)
传递给 Promise.all 的promise并不是一个个的顺序执行的,而是同时开始、并行执行的。
Promise.race
promise.race([promise1, promise2]).then(resolve, reject)
示例
// `delay`毫秒后执行resolve function timerPromisefy(delay) { return new Promise(function (resolve) { setTimeout(function () { resolve(delay); }, delay); }); } // 任何一个promise变为resolve或reject 的话程序就停止运行 Promise.race([ timerPromisefy(1), timerPromisefy(32), timerPromisefy(64), timerPromisefy(128) ]).then(function (value) { console.log(value); // => 1 });
只要有一个promise对象进入 FulFilled 或者 Rejected 状态的话,就会继续进行后面的处理。
Promise polyfill & Test
promise-polyfill.js
学习完Promise后,必定要重写Promise,后续遇到浏览器环境不支持也可有的放矢
代码如下
/** * @author chenchangyuan * @date 2019-02-23 * */ function Promise(executor){ if(typeof executor !== 'function'){ throw new Error('executor is not a function'); } var self = this; self.state = 'pending';//pending fulfilled rejected self.value = null; self.reason = null; self.callbackResolveFn = []; self.callbackRejectFn = []; function resolve(value){ if(self.state === 'pending'){ self.state = 'fulfilled'; self.value = value; self.callbackResolveFn.forEach(function(fn){ fn(); }); } } function reject(reason){ if(self.state === 'pending'){ self.state = 'rejected'; self.reason = reason; self.callbackRejectFn.forEach(function(fn){ fn(); }); } } try{ executor(resolve, reject); }catch(err){ reject(err); } } //回溯函数 function resolvePromise(promise, x, resolve, reject){ if(promise === x) return reject(new TypeError('循环引用')); var flag = false; if(x !== null && (typeof x === 'object' || typeof x === 'function')){ try{ var then = x.then; if(typeof then === 'function'){ then.call(x, function(val){ if(flag) return; flag = true; resolvePromise(promise, val, resolve, reject); }, function(err){ if(flag) return; flag = true; reject(err); }); }else{ resolve(x); } } catch(err){ if(flag) return; flag = true; reject(err); } }else{ resolve(x); } } //返回一个新的promise(pending:push(fn),fulfilled:resolve(val),rejected:reject(reason)) Promise.prototype.then = function(onFulfilled, onRejected){ onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function(value){ return value; }; onRejected = typeof onRejected === 'function' ? onRejected : function(err){ throw new Error(err); }; var self = this, promise2; if(self.state === 'fulfilled'){ promise2 = new Promise(function(resolve, reject){ setTimeout(function(){ try{ //将x处理成一个原始值 var x = onFulfilled(self.value); resolvePromise(promise2, x, resolve, reject); } catch(e){ reject(e); } }) }) } if(self.state === 'rejected'){ promise2 = new Promise(function(resolve, reject){ setTimeout(function(){ try{ //将x处理成一个原始值 var x = onRejected(self.reason); resolvePromise(promise2, x, resolve, reject); } catch(e){ reject(e); } }) }) } if(self.state === 'pending'){ promise2 = new Promise(function(resolve, reject){ self.callbackResolveFn.push(function(){ setTimeout(function(){ try{ //将x处理成一个原始值 var x = onFulfilled(self.value); resolvePromise(promise2, x, resolve, reject); } catch(e){ reject(e); } }) }); self.callbackRejectFn.push(function(){ setTimeout(function(){ try{ //将x处理成一个原始值 var x = onRejected(self.reason); resolvePromise(promise2, x, resolve, reject); } catch(e){ reject(e); } }) }); }) } return promise2; } Promise.prototype['catch']= function (callback) { return this.then(undefined, callback) } Promise.all = function (promises) { return new Promise(function (resolve, reject) { let arr = []; let i = 0; function processData(index, y) { arr[index] = y; if (++i === promises.length) { resolve(arr); } } for (let i = 0; i < promises.length; i++) { promises[i].then(function (y) { processData(i, y) }, reject) } }) } Promise.race = function (promises) { return new Promise(function (resolve, reject) { for (var i = 0; i < promises.length; i++) { promises[i].then(resolve,reject) } }); } Promise.resolve = function(value){ return new Promise(function(resolve,reject){ resolve(value); }); } Promise.reject = function(reason){ return new Promise(function(resolve,reject){ reject(reason); }); } Promise.defer = Promise.deferred = function () { var d = {}; d.promise = new Promise(function (resolve, reject) { d.resolve = resolve; d.reject = reject; }); return d } module.exports = Promise;
promise-aplus-tests
由于是参(抄)考(袭)前辈的polyfill,自己编码测试时出现了两处错误,ES6 Promise 规范的2.3.1和2.3.4
2.3.1
2.3.4
经过改正测试成功
后记
你们的支持是我最大的动力,熬夜码字不易,如果此文对你有帮助,请不吝star--->https://github.com/chenchangyuan/promise
有兴趣加笔者好友的同学请扫描下方二维码(1.本人微信,2.微信公众号,3.技术交流微信群),愿与您成为好友共同探讨技术,畅聊生活!
参考资料
http://liubin.org/promises-book
https://juejin.im/post/5ab20c58f265da23a228fe0f