前言

从事前端的朋友或多或少的接触过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. 34 文件地理数据库(GDB)变文件夹了怎么办

    我们都知道文件地理数据库(GDB)是ArcGIS软件特有的格式,有其独特的设计之处,在文件资源管理器中显示为.gdb的文件夹,但是里面的文件却看不明白,而且将gdb文件夹在Windows资源管理器中直 ...

  2. 32 ArcToolBox学习系列之数据管理工具箱——属性域(Domains)的两种创建及使用方式

    属性域分为两类,一种是范围域,一种是编码的值,下面将两个一起介绍,其中涉及到的编码,名称,只是试验,并非真实情况. 一.首先新建一个文件型地理数据库,将数据导入或者是新建要素类都可以 二.打开ArcT ...

  3. 使用ANY和ALL条件

    在比较运算符中,可以出现ALL和ANY,表示“全部”和“任一”,但是ALL和ANY不能单独使用,需要配合单行比较操作符>.>=.<.<=一起使用.其中: > ANY : ...

  4. 配置NFS固定端口

    NFS启动时会随机启动多个端口并向RPC注册,为了设置安全组以及iptables规则,需要设置NFS固定端口.NFS服务需要开启 mountd,nfs,nlockmgr,portmapper,rquo ...

  5. angular5与angular6的比较

  6. SQL优化方法:

    1.查看连接对象 1 USE master 2 GO 3 --如果要指定数据库就把注释去掉 4 SELECT * FROM sys.[sysprocesses] WHERE [spid]>50 ...

  7. JDK 8 之 Stream sorted() 示例

    原文链接:http://www.concretepage.com/java/jdk-8/java-8-stream-sorted-example 国外对Java8一系列总结的不错, 翻译过来给大家共享 ...

  8. [Swift]LeetCode828. 独特字符串 | Unique Letter String

    A character is unique in string S if it occurs exactly once in it. For example, in string S = " ...

  9. [Swift]LeetCode942. 增减字符串匹配 | DI String Match

    Given a string S that only contains "I" (increase) or "D" (decrease), let N = S. ...

  10. Java 生成 JNI 头文件

    [参考文章]:javah javac 命令行生成native的方法教程 [参考文章]:用javah 导出类的头文件, 常见的错误及正确的使用方法 项目需要调用 C++动态库,由于项目包结构发生变化,需 ...