前言

从事前端的朋友或多或少的接触过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.技术交流微信群),愿与您成为好友共同探讨技术,畅聊生活!

参考资料

https://promisesaplus.com/

http://liubin.org/promises-book

https://juejin.im/post/5ab20c58f265da23a228fe0f

												

前端异步技术之Promise的更多相关文章

  1. 前端异步编程之Promise和async的用法

    传统的异步解决方案采用回调函数和事件监听的方式,而这里主要记录两种异步编程的新方案: ES6的新语法Promise ES2017引入的async函数 Generator函数(略) Promise的含义 ...

  2. 异步编程之Promise(3):拓展进阶

    异步编程系列教程: (翻译)异步编程之Promise(1)--初见魅力 异步编程之Promise(2):探究原理 异步编程之Promise(3):拓展进阶 异步编程之Generator(1)--领略魅 ...

  3. ES7前端异步玩法:async/await理解

    在最新的ES7(ES2017)中提出的前端异步特性:async.await. 什么是async.await? async顾名思义是"异步"的意思,async用于声明一个函数是异步的 ...

  4. JS异步编程 (2) - Promise、Generator、async/await

    JS异步编程 (2) - Promise.Generator.async/await 上篇文章我们讲了下JS异步编程的相关知识,比如什么是异步,为什么要使用异步编程以及在浏览器中JS如何实现异步的.最 ...

  5. ES7前端异步玩法:async/await理解 js原生API妙用(一)

    ES7前端异步玩法:async/await理解   在最新的ES7(ES2017)中提出的前端异步特性:async.await. 什么是async.await? async顾名思义是“异步”的意思,a ...

  6. react 前端项目技术选型、开发工具、周边生态

    react 前端项目技术选型.开发工具.周边生态 声明:这不是一篇介绍 React 基础知识的文章,需要熟悉 React 相关知识 主架构:react, react-router, redux, re ...

  7. ES7中前端异步特性:async、await。

    在最新的ES7(ES2017)中提出的前端异步特性:async.await. 什么是async.await? async顾名思义是"异步"的意思,async用于声明一个函数是异步的 ...

  8. Web前端开发大系概览 (前端开发技术栈)

    前言 互联网建立50多年了,网站开发技术日新月异,但web前端始终离不开浏览器,最终还是HTML+JavaScript+CSS这3个核心,围绕这3个核心而开发出来大量技术框架/解决方案. 我从2000 ...

  9. 通往成功的钥匙--Web前端开发技术

    互联网是一个服务性行业,用户对网站良好的体验度,直接影响到网站的效果.无论你做了多少广告推广,没有用户体验度等于零.Web前端技术是为了解决用户体验度而诞生的.无论是百度.新浪.阿里巴巴等大型网站,还 ...

随机推荐

  1. jQuery 动态绑定插件livequery的用法

  2. TypeScript 函数-重载

    function attr(name:string):string; function attr(age:number):string; function attr(nameorage:any):an ...

  3. 对Jpa中Entity关系映射中mappedBy的理解

    mappedBy 单向关系不需要设置该属性,双向关系必须设置,避免双方都建立外键字段数据库中1对多的关系,关联关系总是被多方维护的即外键建在多方,我们在单方对象的@OneToMany(mappedby ...

  4. IIS 接口访问404

    IIS->网站->ASP->启用父路径(true)

  5. java代码编译与C/C++代码编译的区别

    Java编译原理 1.Java编译过程与c/c++编译过程不同 Java编译程序将java源程序编译成jvm可执行代码--java字节码. Java在编译过程中一般会按照以下过程进行: (1)JDK根 ...

  6. jsp页面的地址

    1. ${pageContext.request.contextPath}是JSP取得绝对路径的方法,等价于<%=request.getContextPath()%> . 也就是取出部署的 ...

  7. 用简单的代码让一组静态图片变成gif动画

    比如这组图片:     变成这样的gif动画:   是不是很神奇....   先看html .样式.很简单,一个div,然后引入图片.   <!DOCTYPE html> <html ...

  8. C# 开源组件--NPOI读取Excel单元格中的公式值

    今天在项目中碰到了EXCEL导入的数据是用公式生成,直接导入不了数据,写在博客中方便自已查询也可以给想找这方面的参考一下: 用NPOI导入时,在OFFICE 2007中的文件导入时一般会用XSSF,所 ...

  9. node环境使用multer搭建一个图片接收服务器

    为了测试图片上传插件的上传功能是否好用,最近尝试搭建了一个接收图片的服务器,因为图片上传的编码格式是form-data,所以我选择使用express+multer,实现过程中发现有几个需要注意的地方, ...

  10. [Swift]LeetCode413. 等差数列划分 | Arithmetic Slices

    A sequence of number is called arithmetic if it consists of at least three elements and if the diffe ...