终于有时间写这篇文章了, 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 类库相关推荐

  1. 手写一个promise用法_手写一个 Promise

    1 js 的基本数据类型? 2 JavaScript 有几种类型的值? 3 什么是堆?什么是栈?它们之间有什么区别和联系? 4 内部属性 [Class] 是什么? 5 介绍 js 有哪些内置对象? 6 ...

  2. 手写一个promise用法_手写一个Promise

    JS面向对象 在JS中一切皆对象,但JS并不是一种真正的面向对象(OOP)的语言,因为它缺少类(class)的概念.虽然ES6引入了class和extends,使我们能够轻易地实现类和继承.但JS并不 ...

  3. 未能加载文件或程序集或它的某一个依赖项_手写一个miniwebpack

    前言 之前好友希望能介绍一下 webapck 相关的内容,所以最近花费了两个多月的准备,终于完成了 webapck 系列,它包括一下几部分: webapck 系列一:手写一个 JavaScript 打 ...

  4. 如何写一个脚本语言_如何写一个Nx schematic plugin?

    前言 玩过Angular的同学都知道Angular作为一个Framework,拥有一套完备的生态,还集成了强大的CLI.而React则仅仅是一个轻量级的Library,官方社区只定义了一套组件的周期规 ...

  5. promise用法_图解 Promise 实现原理(四):Promise 静态方法实现

    作者:Morrain 转发链接:https://mp.weixin.qq.com/s/Lp_5BXdpm7G29Z7zT_S-bQ 前言 Promise 是异步编程的一种解决方案,它由社区最早提出和实 ...

  6. promise用法_【JavaScript 教程】异步操作——Promise 对象

    作者 | 阮一峰 概述 Promise 对象是 JavaScript 的异步操作解决方案,为异步操作提供统一接口.它起到代理作用(proxy),充当异步操作与回调函数之间的中介,使得异步操作具备同步操 ...

  7. promise用法_图解 Promise 实现原理(一)—— 基础实现

    本文首发于 vivo互联网技术 微信公众号 链接: https://mp.weixin.qq.com/s/UNzYgpnKzmW6bAapYxnXRQ 作者:孔垂亮 很多同学在学习 Promise 时 ...

  8. 画一个圆角多边形_用SolidWorks一个扫描画出这个多边形瓶子

    多边形瓶子 2020年10月文件分享 后台发送:2010 获取文件 或底部阅读原文输入提取码 提取码:2010 建模过程 1.在[上视基准面]画两个八边形. 1-1.用样条曲线连接各个端点.(扫描的引 ...

  9. fortran中call的用法_手写源码系列(一)——call、apply、bind

    什么是手写源码 平时面试时经常会遇到让手写一个已有方法的实现,其实面试官是想考察你对于JS底层逻辑是否熟悉,经常面试会出的会在下面: call.apply.bind promise requireJS ...

最新文章

  1. CMake一次失败应用
  2. CPictureEx类实现GIF图片的缩放
  3. js三进制计算机,js 笔记 - 二进制位运算符
  4. RT-Thread pin设备驱动代码结构剖析
  5. 性能测试工具比较:LoadRunner vs JMeter - 测试结果数据比较
  6. HttpClient 指南思维导图笔记
  7. pytorch 查看参数是否被训练 require_grad()
  8. 802.11n协议解析(一)
  9. win7屏保时间设置_015时间轮盘电脑版
  10. (转) Playing FPS games with deep reinforcement learning
  11. NYOJ 972 核桃的数量(蓝桥杯)
  12. MongoDB下载安装教程
  13. 给刚做网站不久的草根站长们
  14. 计蒜客 17119 Trig Function(切比雪夫多项式)
  15. Win10 安装 SQL Server 2008 与使用指南
  16. android仿ppt,android 仿ppt进入动画效果合集
  17. 《从0到1:CTFer成长之路》1.3 任意文件读取漏洞
  18. 两台电脑怎么文件互传,电脑和电脑互传文件怎么传,两台电脑怎么互传文件
  19. 专利驳回后复审费可以减缓
  20. VBA从多张工作簿(workbooks)中多张工作表(worksheets)同一位置提取数据

热门文章

  1. 破环计算机系统的案件量刑,破坏计算机信息系统罪如何定罪量刑
  2. python怎么测试uwsgi并发量_nginx + uWSGI 为 django 提供高并发
  3. 大学计算机专业全民,计算机专业大学排名实力顺序(上大学国内计算机专业大学哪个好值得报读)...
  4. mysql old key files_mysql出现“Incorrect key file for table”解决办法
  5. replace为undefined_手写 XML 转化为 JS对象 方法
  6. epoch训练时间不同_神经网络训练的三个基本概念Epoch, Batch, Iteration
  7. go字符串转byte_go语言学习-基本数据类型
  8. 【论文阅读】Drug Similarity Integration Through Multi-view Graph Auto-Encoders | day4、5
  9. 比python好_这就是为什么Python比R更好的原因
  10. java编译时注解_简单介绍 Java 中的编译时注解