博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Promise用法 & Promise原理实现
阅读量:5740 次
发布时间:2019-06-18

本文共 12302 字,大约阅读时间需要 41 分钟。

概述

Promise最早由社区提出并实现,典型的一些库有Q,when, bluebird等;它们的出现是为了更好地解决JavaScript中异步编程的问题,传统的异步编程最大的特点就是地狱般的回调嵌套,一旦嵌套次数过多,就很容易使我们的代码难以理解和维护。而Promise则可以让我们通过链式调用的方法去解决回调嵌套的问题,使我们的代码更容易理解和维护,而且Promise还增加了许多有用的特性,让我们处理异步编程得心应手

Promise创建

Promise本意是承诺,它有三个状态(等待态、成功态、失败态), 默认是等待态。new Promise 时传递一个执行器 (executor),且执行器立即执行

//Promise是一个类console.log(2)new Promise(function ((resolve, reject) => {    console.log(1)))console.log(3);输出结果如下:213复制代码

Promise.then

每个promise实例都一个then方法,then方法有两个参数(成功和失败),成功会有成功的值,失败会有失败的原因

let promise = new Promise((resolve, reject) => {    //resolve('hello');    reject('hello')});promise.then(data => {    console.log('data', data);}, err=>{    console.log('err', err)})输出结果如下://data helloerr hello复制代码

且同一个promise可以then多次

let promise = new Promise((resolve, reject) => {    //resolve('hello');    reject('hello')});promise.then(data => {    console.log('data', data);}, err=>{    console.log('err', err)});promise.then(data => {    console.log('data', data);}, err=>{    console.log('err', err)})输出结果如下://data hello//data helloerr helloerr hello复制代码

基本的promise实现

Promise/A+ 规范的内容比较多,详情查看,实现 Promise 逻辑时会根据实现的部分介绍相关的Promise/A+规范内容。

class Promise {    constructor(executor) {        //默认的状态        this.status = 'pending';        //成功的值         this.value = undefined;        //失败的原因        this.reason = undefined;        //成功存放的数组        this.onResolvedCallbacks = [];        //失败存放的数组         this.onRejectedCallbacks = [];        //默认让执行器执行        let resolve = (value) => {            if (this.status === 'pending') {                this.status = 'fulfilled'; //成功                this.value = value;                this.onResolvedCallbacks.forEach(fn => fn());             }        }        let reject = (reason) => {            if (this.status === 'pending') {                this.status = 'rejected'; //失败                this.reason = reason;                this.onRejectedCallbacks.forEach(fn => fn());            }        }        try{            executor(resolve, reject);        }catch(e) {            reject(e);        }    }    then(onFufilled, onRejected) {        if (this.status === 'fulfilled') {            onFufilled(this.value);        }        if (this.status === 'rejected') {            onRejected(this.reason);        }          if (this.status === 'pending') {            this.onResolvedCallbacks.push(() => {                onFufilled(this.value);            });            this.onRejectedCallbacks.push(() => {                onRejected(this.reason);            })        }    }}module.exports = Promise;复制代码

验证实现的promise

example 1let Promise = require('./Promise.js')let promise = new Promise((resolve, reject) => {    resolve('hello')});promise.then(data => {    console.log('data', data);}, err=>{    console.log('err', err)});输出结果如下data helloexample 2let Promise = require('./Promise.js')let promise = new Promise((resolve, reject) => {    setTimeout(() => {       resolve('hello')    })});promise.then(data => {    console.log('data', data);}, err=>{    console.log('err', err)});输出结果如下data hello复制代码

Promise链式调用

  • Promise内部抛出异常,直接走失败执行函数
let fs = require('fs');function read (path, enconding) {    return new Promise((resolve, reject) => {        throw new Error('内部抛出异常');        fs.readFile(path, enconding, (err, data) => {            if (err) reject(err);            resolve(data);        })    })}read('./3.txt', 'utf8').then(data => {    console.log(data);}, err => {    console.log("error", err)})输出结果如下:error Error: 内部抛出异常复制代码
  • Promise解决多个异步回调(有关联)
//成功的回调,或者失败的回调执行后返回一个promise//会将这个promise的结果传给下一个then中//如果返回普通值,会将这个普通值传递到下一次then的成功参数read('./1.txt', 'utf8').then(data => {    console.log('then1', data)    return read(data, 'utf8');}, err => {    console.log("err1", err)}).then(data => {    console.log('then2', data);}, err => {    console.log("err2", err)});注: 1.txt 文本内容为 "2.txt"    2.txt 文本内容为 "数据"输出结果如下:then1 2.txtthen2 数据 read('./1.txt', 'utf8').then(data => {    console.log('then1', data)    return read(data, 'utf8');}, err => {    console.log("err1", err)}).then(data => {    console.log('then2', data);    return data}, err => {    console.log("err2", err)}).then(data => {    console.log('then3', data)}).then(data => {    console.log('then4', data)});输出结果如下:then1 2.txtthen2 数据 then3 数据 then4 undefinedread('./1.txt', 'utf8').then(data => {    console.log('then1', data)    return read(data, 'utf8');}, err => {    console.log("err1", err)}).then(data => {    console.log('then2', data);    return data}, err => {    console.log("err2", err)}).then(data => {    console.log('then3', data)}).then(data => {    console.log('then4', data);    throw new Error('xxxxxxxxxxxxxxxx');}).then(null, err => {    console.log("err5", err)}).then(data => {    console.log('then5 成功', data)}, err=> {    console.log("err6 失败")})输出结果如下then1 2.txtthen2 数据 then3 数据 then4 undefinederr5 Error: xxxxxxxxxxxxxxxxthen5 成功 undefinedread('./1.txt', 'utf8').then(data => {    console.log('then1', data)    return read(data, 'utf8');}, err => {    console.log("err1", err)}).then(data => {    console.log('then2', data);    return data}, err => {    console.log("err2", err)}).then(data => {    console.log('then3', data)}).then(data => {    console.log('then4', data);    throw new Error('xxxxxxxxxxxxxxxx');}).then().then(data => {    console.log('then5 成功', data)}).catch(err => {    console.log("catch", err)})输出结果如下then1 2.txtthen2 数据 then3 数据 then4 undefinedcatch Error: xxxxxxxxxxxxxxxx复制代码
  • Promise解决多个异步回调(无关联)
Promise.all 多个promise都成功后,执行成功回调,如果有一个失败,走失败回调
let fs = require('fs');function read(path, enconding) {    return new Promise((resolve, reject) => {        fs.readFile(path, enconding, (err, data) => {            if (err) reject(err);            resolve(data);        })    })}let pRead1 = read('./1.txt', 'utf-8');let pRead2 = read('./2.txt', 'utf-8');Promise.all([pRead1, pRead2]).then(res => {    console.log(res);})输出结果如下[ '2.txt', '数据 ' ]复制代码
Promise.race,多个promise执行,谁先执行完,就用先执行的结果
let fs = require('fs');function read(path, enconding) {    return new Promise((resolve, reject) => {        fs.readFile(path, enconding, (err, data) => {            if (err) reject(err);            resolve(data);        })    })}let pRead1 = read('./1.txt', 'utf-8');let pRead2 = read('./2.txt', 'utf-8');Promise.race([pRead2, pRead1]).then(res => {    console.log(res);}, err => {    console.log('err', err)})输出结果如下2.txt复制代码
  • Promise.resolve、Promise.reject
Promise.resolve(123).then(data => {    console.log(data)});Promise.reject('err 123').then(null, err => {    console.log(err)})复制代码

实现Promise链式调用(promise实现链式调用返回一个新的promise)

class Promise {    constructor(executor) {...}    then(onFufilled, onRejected) {        onFufilled = typeof onFufilled === 'function' ? onFufilled : value => value;        onRejected = typeof onRejected === 'function' ? onRejected : err => {throw err;};        let promise2;        promise2 = new Promise((resolve, reject) => {            if (this.status === 'fulfilled') {                setTimeout(() => {                    try{                        let x = onFufilled(this.value);                        resolvePromise(promise2, x, resolve, reject);                    }catch(e) {                        reject(e)                    }                }, 0);                         }            if (this.status === 'rejected') {                setTimeout(() => {                    try{                        let x = onRejected(this.reason);                        resolvePromise(promise2, x, resolve, reject);                    }catch(e) {                        reject(e)                    }                }, 0);            }            if (this.status === 'pending') {                this.onResolvedCallbacks.push(() => {                    setTimeout(() => {                        try{                            let x = onFufilled(this.value);                            resolvePromise(promise2, x, resolve, reject);                        }catch(e) {                            reject(e)                        }                    }, 0);                });                this.onRejectedCallbacks.push(() => {                    setTimeout(() => {                        try{                            let x = onRejected(this.reason);                            resolvePromise(promise2, x, resolve, reject);                         }catch(e) {                            reject(e)                         }                    }, 0);                })            }        });        return promise2;        //将每个 Promise 实例调用 then 后返回的新 Promise 实例称为 promise2,将 then 回调返回的值称为 x;    }}function resolvePromise(promise2, x, resolve, reject) {    /*    如果 promise2 和 x 为同一个对象,由于 x 要将执行成功或失败的结果传递 promise2 的 then 方法回调的参数,    因为是同一个 Promise 实例,此时既不能成功也不能失败(自己不能等待自己完成),造成循环引用,    这种情况下规定应该抛出一个类型错误来回绝;    */    if (promise2 === x) {        return reject(new TypeError('循环引用'))    }    let called;    //如果 x 是一个对象或者函数且不是 null    if (x != null && (typeof x === 'object' || typeof x === 'function')) {        /*         如果 x 是对象,防止 x 是通过 Object.defineProperty 添加 then 属性,并添加 get 和 set 监听,         如果在监听中抛出异常,需要被捕获到        */        try{            let then = x.then //如果是对象,试着取下then                         if (typeof then === 'function') {                //x.then 是一个函数,就当作 x 是一个 Promise 实例,                then.call(x, y => {                    if (called) return;                    called = true;                    //resolve的结果依旧是promise,继续解析                    resolvePromise(promise2, y, resolve, reject);                }, r => {                    if (called) return;                    called = true;                    reject(y);                 })            }else {                //如果 x.then 不是函数,则说明 x 为普通值,直接调用 promise2 的 resolve 方法将 x 传入,                resolve(x);            }        }catch(e) { //取then出错,不在继续执行            if (called) return;            called = true;            reject(e);        }    }else {        //不满足条件说明该返回值就是一个普通值,直接执行 promise2 的 resolve 并将 x 作为参数传入;        resolve(x);    }}module.exports = Promise;复制代码

验证promise链式调用

let Promise = require('./Promise')let promise = new Promise((resolve, reject) => {    resolve('hello');})promise.then(data => {    console.log('then1', data);    return 4}).then(data => {    console.log("data", data)}).then(() => {    throw new Error("error");}).then(() => {    console.log("success");}, err => {    console.log(err);}).then(() => {    console.log("成功");}, () => {    console.log("失败");});输出结果如下then1 hellodata 4Error: error成功复制代码

验证参数是否穿透

let p1 = new Promise((resolve, reject) => resolve("ok"));let p2 = p1.then().then(data => {    console.log(data);    throw new Error("出错了");});p2.then().then(null, err => console.log(err));输出结果如下okError: 出错了复制代码

实现catch方法

class Promise {    constructor(executor) {...}    then(onFufilled, onRejected) {...}    catch(onRejected) {        return this.then(null, onRejected)    }}复制代码

实现Promise.resolve方法

Promise.resolve = function (value) {    return new Promise((resolve, reject) => {        resolve(value);    })}复制代码

实现Promise.reject方法

Promise.reject = function (reason) {    return new Promise((resolve, reject) => {        reject(reason);    })}复制代码

实现Promise.all方法

Promise.all = function (promises) {    return new Promise((resolve, reject) => {        let result = [];        let idx = 0;        let processData = (index, data) => {            idx++;            result[index] = data;            if (promises.length === idx) {                resolove(result)            }        }        for (let i = 0; i < promises.length; i++) {            promises[i].then(data => {                processData(i, data);            }, reject)        }    })}复制代码

实现Promise.race方法

Promise.race = function (promises) {    return new Promise((resolve, reject) => {        for (let i = 0; i < promises.length; i++) {            promises[i].then(resolve, reject)        }    })}复制代码

测试Promise的实现代码(测试包 promises-aplus-tests)

  • 全局安装 npm install promises-aplus-tests -g
promises-aplus-tests 文件
Promise.defer = Promise.deferred = function () {    let dfd = {};    dfd.promise = new Promise((resolve, reject) => {        dfd.resolve = resolve;        dfd.reject = reject;    });    return dfd;}//https://www.pandashen.com/2018/06/13/20180613193626/#more  <-- 更多描述请查看复制代码

转载于:https://juejin.im/post/5b418e12e51d45194d4fc3be

你可能感兴趣的文章
一步一步学Linq to sql(六):探究特性
查看>>
Kubernetes中的Ingress
查看>>
wcf随笔2---callback回调
查看>>
【转载+整理】Android中TouchEvent事件分析
查看>>
中国剩余定理(转)
查看>>
Windows 7专业版安装VS2005与WinCE6.0开发环境
查看>>
Codeforces Round #415 (Div. 1) B. Glad to see you!
查看>>
09.索引-过滤索引
查看>>
Javascript 函数
查看>>
服务器安装 lnmp 跟 lamp (centos7.5)
查看>>
操作系统
查看>>
关于ArcGis for javascript的引用天地图
查看>>
OC-protocol
查看>>
基于ArcGIS for Server的服务部署分析 分类: ArcG...
查看>>
Halcon算子翻译——dev_open_file_dialog
查看>>
使用xsl的原因之一
查看>>
Aizu - ALDS1_8_A Binary Search Tree I(二叉搜索树-插入)
查看>>
SSE指令集系列之二----浮点与整数转换指令
查看>>
Django之Auth认证
查看>>
5-Dalvik垃圾收集机制Cocurrent GC
查看>>