手写一个promise用法_手写一个自己的 JavaScript Promise 类库
终于有时间写这篇文章了, ES2015 推出了JS 的 Promise ,而在没有原生支持的时候,我们也可以使用诸如 Promises/A+ 的库的帮助,在我们的代码里实现Promise 的支持;
如何使用 Promise
在讲具体实现之前我们还是先了解下我们如何使用 Promise 在我们的代码中。
function getData() {
return new Promise((resolve, reject)=>{
request( your_url, (error, res, movieData)=>{
if (error) reject(error);
else resolve(movieData);
});
});
}
// 使用 getData
getData().then(data => console.log(data))
.catch(error => console.log(error));
例子我们需要使用 request 模块去请求一个地址,然后拿到响应的数据。由于 request 过程是一个异步的过程。因此我们使用了 promise 来实现。在使用 promsie 的 then 方法拿到 resolve 回来的的数据。
开始基本实现
首先我们定义一个基本的 Promise 类,为了区别于原生的 Promise 我们可以自定义一个 JPromise 类。
function Promise(fn) {
var callback = null;
this.then = function(cb) {
callback = cb;
};
function resolve(value) {
callback(value);
}
fn(resolve);
}
我们这个时候先添加一些简单的测试用例。
/** test a single plugin */
import { expect } from 'chai';
import JPromise from '../../src/index';
describe('your own promise library unit tests', () => {
let data = 1;
const p = new JPromise((resolve) => {
resolve(5);
});
it('has then method', () => {
expect(typeof p.then === 'function').to.equal(true);
});
it('excute a then function', () => {
p.then((val) => data = val);
expect(data).to.equal(5);
});
});
但是这样写法有一个问题就是你会发现他在 resolve() 会在 then 之前调用。意味着 callback 会是 null .暂时我们用 setTimeout 来 hack 这个问题。
function Promise(fn) {
var callback = null;
this.then = function(cb) {
callback = cb;
};
function resolve(value) {
setTimeout(function() {
callback(value);
}, 1);
}
fn(resolve);
}
但实际我们在测试的过程中,会发现这样只是规避 resolve 先执行的问题,后面会继续说如何解决上面的问题。
再进一步,我们需要 Promise 有状态。
Promise 的状态可以是 pending 等待一个值,也可以是 resolved 返回一个值
一旦 Promise 返回一个值,则后面将不再返回
function JPromise(fn) {
let state = 'pending';
let deferred = null;
let value = null;
function resolve(newVal) {
value = newVal;
state = 'resolved';
if (deferred) {
handle(deferred);
}
}
function handle(onResolved) {
if (state === 'pending') {
deferred = onResolved;
return;
}
onResolved(value);
}
this.then = function(onResolved) {
handle(onResolved);
};
fn(resolve);
}
module.exports = JPromise;
通过状态,我们可以确保每次函数触发 then 方法的时候, resolve 始终都能够被调用到。
似乎这个时候我们的测试可以跑通了。但是,远远没有结束,我们要确保 Promise 的链式调用。
getSomeData()
.then(filterTheData)
.then(processTheData)
.then(displayTheData);
我们可能经常会看到这样的写法。很显然刚刚的不合适了。函数会一次执行 then 方法,然后等待 传入 resolve 里的函数执行完毕,然后执行下一个 then 方法 。
如果熟悉 jQuery 链式调用 的话,可以很好的理解。
我们会在 then 的方法里面返回 Promise 对象。
function JPromise(fn) {
let state = 'pending';
let deferred = null;
let value = null;
function resolve(newVal) {
value = newVal;
state = 'resolved';
if (deferred) {
handle(deferred);
}
}
function handle(handler) {
if (!handler.onResolved) {
handler.resolve(value);
return;
}
const ret = handler.onResolved(value);
handler.resolve(ret);
}
this.then = function(onResolved) {
return new Promise((resolve) => {
handle({
onResolved,
resolve,
});
});
};
fn(resolve);
}
module.exports = JPromise;
从上面代码我们中可以看出,我们在调用 then 的方法适合,始终都会创建一个 Promise 对象。而使用回调的时候则可以避免这样一个问题。
那么第二个 Promise resolve 传入的实际上是第一个 Promise 返回的值。
const ret = handler.onResolved(value);
我们会将函数执行的值,带入下一个 resolve 中。由于 then() 方法始终都是返回一个 Promsie
当然 then() 方法也允许你不传入 callback 进去,比如下面这样:
doSomething().then().then(function(result) {
console.log('got a result', result);
});
当然这些就是程序兼容性的处理。我们需要在没有传入的时候将之前保留的函数进行触发
if(!handler.onResolved) {
handler.resolve(value);
return;
}
对 reject 进行支持
如果遇到问题了,我们需要对这些进行抛出,外部能够获取到这些问题。
doSomething().then(function(value) {
console.log('Success!', value);
}, function(error) {
console.log('Uh oh', error);
});
如同上面实现的一样我们需要将内部的 state 置位 rejected。
function doSomething() {
return new Promise(function(resolve, reject) {
var result = somehowGetTheValue();
if(result.error) {
reject(result.error);
} else {
resolve(result.value);
}
});
}
参考 resolve 的实现,
function JPromise(fn) {
let state = 'pending';
let deferred = null;
let value = null;
function resolve(newVal) {
value = newVal;
state = 'resolved';
if (deferred) {
handle(deferred);
}
}
function reject(reason) {
state = 'rejected';
value = reason;
if (deferred) {
handle(deferred);
}
}
function handle(handler) {
if (state === 'pending') {
deferred = handler;
return;
}
let handlerCallback;
if (state === 'resolved') {
handlerCallback = handler.onResolved;
} else {
handlerCallback = handler.onRejected;
}
if (!handlerCallback) {
if (state === 'resolved') {
handler.resolve(value);
} else {
handler.reject(value);
}
return;
}
const ret = handlerCallback(value);
handler.resolve(ret);
}
this.then = function(onResolved, onRejected) {
return new Promise((resolve, rejected) => {
handle({
onResolved,
onRejected,
resolve,
rejected
});
});
};
fn(resolve, reject);
}
module.exports = JPromise;
实现到这里的时候,我们还需要对异常的错误进行处理;在 Promise 用 catch 进行捕获。
function resolve(newValue) {
try {
// ... as before
} catch(e) {
reject(e);
}
}
除此之外,我们还得防止 在执行回调的时候,在被调用的过程中出现异常。
function handle(deferred) {
// ... as before
let ret;
try {
ret = handlerCallback(value);
} catch(e) {
handler.reject(e);
return;
}
handler.resolve(ret);
}
似乎感觉实现的差不多了。但是刚才的实现有一个不好的地方,就是 Promise 中用了 try…catch… 。这样 Promise 会吞噬错误。也就是:
function getSomeJson() {
return new Promise(function(resolve, reject) {
var badJson = "
uh oh, this is not JSON at all!"; resolve(badJson); }); } getSomeJson().then(function(json) { var obj = JSON.parse(json); console.log(obj); }, function(error) { console.log('uh oh', error); });
这个 JSON 解析的时候会出现错误,但实际上我们在控制台看不到这样的错误。如果你想捕获这些错误,你需要添加一个错误处理的函数:
getSomeJson().then(function(json) {
var obj = JSON.parse(json);
console.log(obj);
}).then(null, function(error) {
console.log("an error occured: ", error);
});
Promise 需要异步处理
前面我们提到了使用 setTimeout 去故意延迟执行,随后我们通过 state 去克服了这么一个问题,但是 Promises/A+ 的定义额 promise 的解决方案的确是异步执行的。这样我们可以利用 setTimeout 去包裹 handle 里的 resovle 执行;
function handle(handler) {
if(state === 'pending') {
deferred = handler;
return;
}
setTimeout(function() {
// ... as before
}, 1);
}
实际上,在实现过程中,我们可以使用 setImmediate 而不是 setTimeout。
var promise = doAnOperation();
invokeSomething();
promise.then(wrapItAllUp);
invokeSomethingElse();
上面的代码,我们预期的效果执行流程
doAnOperation -> invokeSomething -> invokeSomethingElse -> wrapItAllUp
然而如果 doAnOperation 并非异步执行的函数的话,可能实际效果是:
doAnOperation -> invokeSomething -> wrapItAllUp -> invokeSomethingElse
这就是为什么 promise 始终都会去异步执行传入的回调。
手写一个promise用法_手写一个自己的 JavaScript Promise 类库相关推荐
- 手写一个promise用法_手写一个 Promise
1 js 的基本数据类型? 2 JavaScript 有几种类型的值? 3 什么是堆?什么是栈?它们之间有什么区别和联系? 4 内部属性 [Class] 是什么? 5 介绍 js 有哪些内置对象? 6 ...
- 手写一个promise用法_手写一个Promise
JS面向对象 在JS中一切皆对象,但JS并不是一种真正的面向对象(OOP)的语言,因为它缺少类(class)的概念.虽然ES6引入了class和extends,使我们能够轻易地实现类和继承.但JS并不 ...
- 未能加载文件或程序集或它的某一个依赖项_手写一个miniwebpack
前言 之前好友希望能介绍一下 webapck 相关的内容,所以最近花费了两个多月的准备,终于完成了 webapck 系列,它包括一下几部分: webapck 系列一:手写一个 JavaScript 打 ...
- 如何写一个脚本语言_如何写一个Nx schematic plugin?
前言 玩过Angular的同学都知道Angular作为一个Framework,拥有一套完备的生态,还集成了强大的CLI.而React则仅仅是一个轻量级的Library,官方社区只定义了一套组件的周期规 ...
- promise用法_图解 Promise 实现原理(四):Promise 静态方法实现
作者:Morrain 转发链接:https://mp.weixin.qq.com/s/Lp_5BXdpm7G29Z7zT_S-bQ 前言 Promise 是异步编程的一种解决方案,它由社区最早提出和实 ...
- promise用法_【JavaScript 教程】异步操作——Promise 对象
作者 | 阮一峰 概述 Promise 对象是 JavaScript 的异步操作解决方案,为异步操作提供统一接口.它起到代理作用(proxy),充当异步操作与回调函数之间的中介,使得异步操作具备同步操 ...
- promise用法_图解 Promise 实现原理(一)—— 基础实现
本文首发于 vivo互联网技术 微信公众号 链接: https://mp.weixin.qq.com/s/UNzYgpnKzmW6bAapYxnXRQ 作者:孔垂亮 很多同学在学习 Promise 时 ...
- 画一个圆角多边形_用SolidWorks一个扫描画出这个多边形瓶子
多边形瓶子 2020年10月文件分享 后台发送:2010 获取文件 或底部阅读原文输入提取码 提取码:2010 建模过程 1.在[上视基准面]画两个八边形. 1-1.用样条曲线连接各个端点.(扫描的引 ...
- fortran中call的用法_手写源码系列(一)——call、apply、bind
什么是手写源码 平时面试时经常会遇到让手写一个已有方法的实现,其实面试官是想考察你对于JS底层逻辑是否熟悉,经常面试会出的会在下面: call.apply.bind promise requireJS ...
最新文章
- CMake一次失败应用
- CPictureEx类实现GIF图片的缩放
- js三进制计算机,js 笔记 - 二进制位运算符
- RT-Thread pin设备驱动代码结构剖析
- 性能测试工具比较:LoadRunner vs JMeter - 测试结果数据比较
- HttpClient 指南思维导图笔记
- pytorch 查看参数是否被训练 require_grad()
- 802.11n协议解析(一)
- win7屏保时间设置_015时间轮盘电脑版
- (转) Playing FPS games with deep reinforcement learning
- NYOJ 972 核桃的数量(蓝桥杯)
- MongoDB下载安装教程
- 给刚做网站不久的草根站长们
- 计蒜客 17119 Trig Function(切比雪夫多项式)
- Win10 安装 SQL Server 2008 与使用指南
- android仿ppt,android 仿ppt进入动画效果合集
- 《从0到1:CTFer成长之路》1.3 任意文件读取漏洞
- 两台电脑怎么文件互传,电脑和电脑互传文件怎么传,两台电脑怎么互传文件
- 专利驳回后复审费可以减缓
- VBA从多张工作簿(workbooks)中多张工作表(worksheets)同一位置提取数据
热门文章
- 破环计算机系统的案件量刑,破坏计算机信息系统罪如何定罪量刑
- python怎么测试uwsgi并发量_nginx + uWSGI 为 django 提供高并发
- 大学计算机专业全民,计算机专业大学排名实力顺序(上大学国内计算机专业大学哪个好值得报读)...
- mysql old key files_mysql出现“Incorrect key file for table”解决办法
- replace为undefined_手写 XML 转化为 JS对象 方法
- epoch训练时间不同_神经网络训练的三个基本概念Epoch, Batch, Iteration
- go字符串转byte_go语言学习-基本数据类型
- 【论文阅读】Drug Similarity Integration Through Multi-view Graph Auto-Encoders | day4、5
- 比python好_这就是为什么Python比R更好的原因
- java编译时注解_简单介绍 Java 中的编译时注解