一个 Promise 的运用:

var firstPromise = new Promise(function(resolve,reject){
setTimeout(function(){
var result = Math.random() <= 0.5 ? 1:0;
if(result){
resolve('resolved');
}else{
reject('rejected')
}
},1000)
}) var secondPromise = new Promise(function(resolve,reject){
setTimeout(function(){
var result = Math.random() <= 0.5 ? 1:0;
if(result){
resolve('resolved');
}else{
reject('rejected')
}
},2000)
}) firstPromise.then(function(value){
console.log(value);
return secondPromise;
},function(reason){
console.log(reason);
return secondPromise;
}).then(function(value){
console.log(value);
},function(reason){
console.log(reason);
}) // 1s后随机输出结果 resolved 或者 rejected
// 再1s后随机输出结果 resolved 或者 rejected

效果如上,在一个 promise 被完成/被拒绝时执行对应的回调取到异步结果。

同时,以上代码使用 promise 避免了回调地狱,规范了回调操作。

接下来,把 promise 拆成几块,学习一下怎么样的实现过程。

步骤一、Promise 构造函数

创建 promise 对象的构造函数,是创造 promise 的工厂。

基础要求:Promise 函数仅产生一个对象,避免大量变量的污染,将该藏好的对象/值藏好,该暴露的暴露;Promise 接收一个函数作为参数,且该函数在构造 promise 对象时被执行;Promise 必须有个 .then 方法(后续方法可自行扩展)。

function Promise(fn){
this.then = function(){ };
}

步骤二、初始化过程,处理参数fn

Promise 构造函数参数 fn 中传入 resolve/reject;Promise 初始化的时候执行 fn 并在 promise 得到最终结果后执行传入的 resolve/reject ;resolve/reject 函数中执行 promise 中指定的完成/拒绝时回调函数,并以最终结果作为参数。

function Promise(fn){

    // 完成时
function resolve(value) {
console.log('value ',value);
} // 拒绝时
function reject(reason) {
console.log('reason ',reason);
} // 执行传入的fn
function init(fn, onResolved, onRejected) {
try {
fn(function (value) {
onResolved(value);
}, function (reason) {
onRejected(reason);
})
} catch (err) {
onRejected(err);
}
} init(fn, resolve, reject);     this.then = function(){ };
} var promise = new Promise(function(resolve,reject){
setTimeout(function(){
var result = Math.random() <= 0.5 ? 1:0;
if(result){
resolve('resolved')
}else{
reject('rejected')
}
},1000)
}) // 1s后随机输出 value resolved 或者 reason rejected

步骤三、.then 里的处理流程

在promise中, .then 将传入的 resolvedHandle 和 rejectedHandle 函数存入 promise 的 handlers 中作为回调列表中的一项,在需要的时候(Promise被完成的时候)携带最终结果执行。

首先,假设有个异步操作,而且已经知道回调函数是什么,代码如下:

var resolvedHandle = function(res){ console.log(res) };
var rejectedHandle = function(err){ console.log(err) }; setTimeout(function(){
var result = Math.random() <= 0.5 ? 1:0;
if(result){
resolvedHandle('resolved');
}else{
rejectedHandle('rejected');
}
},1000) // 1s后输出 resolved 或者 rejected

而对于 promise 而言,回调函数是在 .then 中传入并且在 promise 中给定义的,并且为了实现链式的操作, .then 中必须有返回一个对象,且对象须是一个携带 .then 方法的对象或函数或为一个 promise ,才足以继续执行.then。

// fn 作为初始化Promise时传入的函数,应该被立即执行并取出其中的调用
function Promise(fn) { var $resolveHandle = function (res) { };
var $rejectHandle = function (err) { }; // 执行Promise被完成时函数
function resolve(value) {
try {
var then = getThen(value);
if (then) {
init(then.bind(value), resolve, reject);
return;
};
fulfill(value);
} catch (err) {
reject(err);
}
} // 完成时
function fulfill(value) {
$resolveHandle(value);
$resolveHandle = null;
} // 拒绝时
function reject(reason) {
$rejectHandle(reason);
$rejectHandle = null;
} // 执行传入的fn并执行回调
function init(fn, onResolved, onRejected) {
try {
fn(function (value) {
onResolved(value);
}, function (reason) {
onRejected(reason);
})
} catch (err) {
onRejected(err);
}
} init(fn, resolve, reject); function getThen(value) {
var t = typeof value;
if (value && (t === 'object' || t === 'function')) {
var then = value.then;
if (typeof then === 'function') {
return then;
}
}
return null;
}; this.then = function (resolveHandle, rejectHandle) {
return new Promise(function (resolve, reject) {
$resolveHandle = function (result) {
resolve(resolveHandle(result));
}
$rejectHandle = function (reason) {
if(rejectHandle){
resolve(rejectHandle(reason));
}else{
reject(reason)
}
}
})
}
} var firstPromise = new Promise(function (resolve, reject) {
setTimeout(function () {
var result = Math.random() <= 0.5 ? 1 : 0;
if (result) {
resolve('resolved');
} else {
reject('rejected');
}
}, 1000);
}) var secondPromise = new Promise(function (resolve, reject) {
setTimeout(function () {
var result = Math.random() <= 0.5 ? 1 : 0;
if (result) {
resolve('resolved 2');
} else {
reject('rejected 2');
}
}, 2000);
}) firstPromise.then(function (res) {
console.log('res ', res);
return secondPromise;
}).then(function (res) {
console.log('res 2 ', res);
}, function (err) {
console.log('rej 2 ', err);
})
// 1s后随机输出 res resolved 或者 rej rejected
// 又1s后输出 res 2 resolved 2 或者 rej 2 rejected 2 或者 rej 2 rejected

至此,上面的代码基本算是满足了一个 promise 的实现思路,但离正规军 promise 实现还存在一段距离

o(╥﹏╥)o...接下去学习吧。

步骤四、Promise/A+规范

由于 Promise/A+规范较长,就不放到文章里了,给链接吧(中午版是自己翻译的,有出入的地方还请以英文原版为准)

Promise/A+ 规范 [ 原文 ]

Promise/A+ 规范 [ 译文 ]

对照promise/A+规范,以上的Promise代码还存在问题:

  1.promise还需要存储promise状态和最终结果,以便后续被多次使用;

  2.同一个promise的.then方法中注册的回调函数可被多次执行,且回调函数可以是个列表;

  3.事件调度,回调函数应该在本轮.then方法所在事件队列结束后被调用;

  4.捕捉错误并做拒绝处理;

  更多细节...

继续改进,最后整改后的代码大致是这样的:

function Promise(fn) {
/* state
* 0 : pending
* 1 : resloved
* 2 : rejected
*/
var state = 0;
var value = null;
var handlers = []; function fulfill(result) {
state = 1;
value = result;
handlers.forEach(handle);
handlers = [];
}; function reject(error) {
state = 2;
value = error;
handlers.forEach(handle);
handlers = [];
}; function resolve(result) {
try {
var then = getThen(result);
if (then) {
init(then.bind(result), resolve, reject);
return;
}
fulfill(result);
} catch (err) {
reject(err);
}
}; function getThen(value) {
var type = typeof value;
if (value && (type === 'object' || type === 'function')) {
var then = value.then;
if (typeof then === 'function') {
return then;
}
}
return null;
}; function handle(handler) {
if (state === 0) {
handlers.push(handler);
} else {
if (typeof handler.onResolved === 'function') {
if (state === 1) {
handler.onResolved(value);
};
if (state === 2) {
handler.onRejected(value);
};
}
}
}; // 放到事件队列最后,在本轮事件执行完后执行
function timeHandle(callback, newValue) {
setTimeout(function () {
callback(newValue);
}, 0)
} function init(fn, onResolved, onRejected) {
try {
fn(function (value) {
timeHandle(onResolved, value);
}, function (reason) {
timeHandle(onRejected, reason);
});
} catch (err) {
timeHandle(onRejected, err);
}
}; init(fn, resolve, reject); this.then = function (onResolved, onRejected) {
if (!onResolved && !onRejected) {
throw new TypeError('One of onResolved or onRejected must be a function.')
};
return new Promise(function (resolve, reject) {
handle({
onResolved: function (result) {
if (typeof onResolved === 'function') {
try {
resolve(onResolved(result));
} catch (err) {
reject(err);
}
} else {
resolve(result);
}
},
onRejected: function (error) {
if (typeof onRejected === 'function') {
try {
resolve(onRejected(error));
} catch (err) {
reject(err);
}
} else {
reject(error);
}
}
})
})
};
} var firstPromise = new Promise(function (resolve, reject) {
setTimeout(function () {
var result = Math.random() <= 0.5 ? 1 : 0;
if (result) {
resolve('resolved 1');
} else {
reject('rejected 1');
}
}, 1000);
}) var secondPromise = new Promise(function (resolve, reject) {
setTimeout(function () {
var result = Math.random() <= 0.5 ? 1 : 0;
if (result) {
resolve('resolved 2');
} else {
reject('rejected 2');
}
}, 3000);
}) firstPromise.then(function (res) {
console.log('res 1 ', res);
return secondPromise;
}, function (err) {
console.log('rej 1 ', err);
return secondPromise;
}).then(function (res) {
console.log('res 2 ', res);
}, function (err) {
console.log('rej 2 ', err);
}) /* *
* 1s后输出 res 1 resolved 1 或者 rej 1 rejected 1
* 2s后输出 res 2 resolved 2 或者 rej 2 rejected 2
* */

通过板块一、二、三的知识点,即可大致摸清promise的实现;板块四加上一些补充和限制,遵循一些规范,提高promise功能的可扩展性。

学会了怎么理解promise,更重要的是学会正确的使用它。

正确使用 Promise

promise 在业务开发中多用来处理异步或者多层回调的情况。

基础使用 Promise MDN 及相关介绍文档中的案例为准,这里不一一赘述了... 这里简单的列出两个在使用 promise 过程中比较需要注意的点:

1. 不同平台环境下 Promise 的方法和遵循规则略微有些出入,详情以各平台环境下的 Promise 对象为基准。

  如 es6 Promise 存在Promise.race,Promise.all等方法,node中则没有这些方法。

  如 浏览器 Promise 事件调度走的是 setTimeout,node 走的是 process.nextTick 。(参考 [asap] )

2. Promise 虽可解决回调操作的规范问题(回调地狱),但也不能滥用 Promise (可能会占用过多内存)。

promise 解决后的结果会被存于内存中,被对应 promise 引用着,将上面的最终代码中测试的两个 promise 改成如下:

var firstPromise = new Promise(function (resolve, reject) {
setTimeout(function () {
var result = Math.random() <= 0.5 ? 1 : 0;
var str = '';
var i = 0, num = 500000;
for (; i < num; i++) {
str += 'promise '
}
if (result) {
resolve('resolved 1 : ' + str);
} else {
reject('rejected 1 : ' + str);
}
}, 1000);
})

则内存占用情况如下:

这些是一些平台差异或业务需求方面的不同点,对 Promise 核心实现并影响甚微,对 Promise 扩展方法有影响,对业务中 Promise 的使用有影响。

参考

1. Promise/implementing

2. Promise 实现代码阅读

分步理解 Promise 的实现的更多相关文章

  1. 大白话讲解Promise(二)理解Promise规范

    上一篇我们讲解了ES6中Promise的用法,但是知道了用法还远远不够,作为一名专业的前端工程师,还必须通晓原理.所以,为了补全我们关于Promise的知识树,有必要理解Promise/A+规范,理解 ...

  2. 彻底理解Promise对象——用es5语法实现一个自己的Promise(上篇)

    本文同步自我的个人博客: http://mly-zju.github.io/ 众所周知javascript语言的一大特色就是异步,这既是它的优点,同时在某些情况下也带来了一些的问题.最大的问题之一,就 ...

  3. 理解Promise的三种姿势

    译者按: 对于Promise,也许你会用了,却并不理解:也许你理解了,却只可意会不可言传.这篇博客将从3个简单的视角理解Promise,应该对你有所帮助. 原文: Three ways of unde ...

  4. 理解Promise的3种姿势

    译者按: 对于Promise,也许你会用了,却并不理解:也许你理解了,却只可意会不可言传.这篇博客将从3个简单的视角理解Promise,应该对你有所帮助. 原文: Three ways of unde ...

  5. 理解promise 02

    1:promise是什么? 就是(链式)包装的回调函数. 2:语法 new Promise( function(resolve, reject) {...} /* executor */ ); exe ...

  6. 160701、理解 Promise 的工作原理

    Javascript 采用回调函数(callback)来处理异步编程.从同步编程到异步回调编程有一个适应的过程,但是如果出现多层回调嵌套,也就是我们常说的厄运的回调金字塔(Pyramid of Doo ...

  7. 160623、理解 Promise 的工作原理

    Javascript 采用回调函数(callback)来处理异步编程.从同步编程到异步回调编程有一个适应的过程,但是如果出现多层回调嵌套,也就是我们常说的厄运的回调金字塔(Pyramid of Doo ...

  8. 理解Promise简单实现的背后原理

    在写javascript时我们往往离不开异步操作,过去我们往往通过回调函数多层嵌套来解决后一个异步操作依赖前一个异步操作,然后为了解决回调地域的痛点,出现了一些解决方案比如事件订阅/发布的.事件监听的 ...

  9. 理解promise 01

    原文地址: http://pouchdb.com/2015/05/18/we-have-a-problem-with-promises.html 用Javascript的小伙伴们,是时候承认了,关于 ...

随机推荐

  1. Eclipse环境下如何配置tomcat,并且把项目部署到Tomcat服务器上

    打开Eclipse,单击“Window”菜单,选择下方的“Preferences”. 单击“Server”选项,选择下方的“Runtime Environments”.  点击“Add”添加Tomca ...

  2. 笔记 Bioinformatics Algorithms Chapter1

    Chapter1 WHERE IN THE GENOME DOES DNA REPLICATION BEGIN    一. ·聚合酶启动结构域会结合上游序列的一些位点,这些位点有多个,且特异,并且分布 ...

  3. EndNote安装

    下载地址:https://download.csdn.net/download/noticeable/10283841 EndNote X8.1是一款功能强大的文献管理软件,软件在x8版本中继承了国际 ...

  4. MyBatis高级篇之整合ehcache缓存框架

    MyBatis高级篇之整合ehcache缓存框架  2017-09-01  0 Comments  1,671 Views  0 Times 一.前言 MyBatis为我们提供了Cache接口,也提供 ...

  5. ubuntu apache linux

    在ubuntu下安装的apache, 很多配置文件都分开写了,需要了解一下各部分: http://blog.csdn.net/veizz/article/details/7410784 Ubuntu下 ...

  6. 水池问题的lua语言算法(面试题分析:我的Twitter技术面试失败了)

    twitter面试题内容 “看下面这个图片” “在这个图片里我们有不同高度的墙.这个图片由一个整数数组所代表,数组中每个数是墙的高度.上边的图可以表示为数组[2,5,1,2,3,4,7,7,6]” “ ...

  7. adb push 和 adb pull命令

    adb push命令 :从电脑上传送文件到手机: adb pull命令 :从手机传送文件到电脑上             @Cocos 下次需要权限的目录可以执行chmod 777 目录名      ...

  8. Jquery学习(表单-Button)-----jQuery1.4.2

    <html> <head> <base href="<%=basePath%>"> <title>My JSP 'jqu ...

  9. tensorflow 安装命令

    sudo pip install --upgrade --ignore-installed six tensorflow-0.9.0-py2-none-any.whl

  10. mysql命令行客户端结果分页浏览

    转载请注明出处:http://xiezhenye.com/2008/06/mysql%e5%91%bd%e4%bb%a4%e8%a1%8c%e5%ae%a2%e6%88%b7%e7%ab%af%e7% ...