js手写一个实现一个简单的promise__小前端chenMr

1.首先来看一下promise常用场景

function chenPromise (arg) {
   return new Promise (resolve, reject) {
      if () {
          doSomeThing(arg)
          resolve(val)
      } else {
          reject(val)
      }
   }
 }chenPromise(arg).then(((val) => {}, (err) =>{})

2.了解promise状态

1.常规分三种:PENDING, RESOLVED, REJECTED

2.一开始promise状态是PENDING,当调用了resolve()之后promise的状态就被固定了即成功时的状态RESOLVED,当调用了reject()之后promise状态就被成了REJECTED了

3.上面说了一旦调用了reject()或者resolve()状态就不能被改变了,这一点也可以算是promise的缺陷吧

4.代码是怎么做到状态从pending到resolove就不会被改变了呢可通过代码如下

_resolve(){       window.addEventListener(‘message‘, (val)=>{
            if (this.status !== KPromise.PENDING) return // 当状态已经被固定了就不会往下执行了
            this.status = KPromise.RESOLVED  // 当执行这个函数的时候状态就已经被固定了

            this.value = val

            let handler;
            while(handler = this.resolveQueues.shift()){
                handler(this.value)
            }
        })}

3.控制.then()在resolve()后面执行

实现思路:当调用.then(resolvehandle,rejecthandle)方法时将resolvehandle和rejecthandle分别push到一个数组中,当resolve()执行时,遍历两个数组unshift()一次一队列先进先出方式依次执行

then(resolveHandler, rejectHandler) {

         this.resolveQueues.push(resolveHandler)
         this.rejectQueues.push(rejectHandler)
  }

reject(){

  let handler;

while(handler = this.rejectQueues.shift()){ // 执行以队列方式(先进先出方式)执行被注册函数

handler(this.value)

}

}

resolve () {  

let handler;

while(handler = this.resolveQueues.shift()){ // 执行以队列方式(先进先出方式)执行被注册函数

handler(this.value)

}

}

3.实现promise先于setTimeout执行

setTimeout(() => {console.log(123)}, 100)

promise().resolve().then(() => {console.log(456)})

结果是先输出456 在输出123

这里实现涉及事件循环以及异步任务的宏认为和微任务执行顺序,通过微任务先于宏认为先执行机制来实现,具体可以通过h5新增的messge方法来达到如下

resolve(val) {

    window.addEventListener(‘message‘, (val)=>{ // 这里涉及到宏任务和微任务的区别,实现让promises里面的方法是先于setTimeout执行的

if (this.status !== KPromise.PENDING) return // 当状态已经被固定了就不会往下执行了

this.status = KPromise.RESOLVED  // 当执行这个函数的时候状态就已经被固定了

this.value = val

let handler;

while(handler = this.resolveQueues.shift()){ // 执行以队列方式(先进先出方式)执行被注册函数

handler(this.value)

}

})

window.postMessage(‘‘) // h5新增

this._finally(this.value)

}

_rejected(val) {

window.addEventListener(‘message‘,()=>{ // 这里涉及到宏任务和微任务的区别,实现让promises里面的方法是先于setTimeout执行的

this.status = KPromise.REJECTED

let handler;

while(handler = this.rejectQueues.shift()){ // 执行以队列方式(先进先出方式)执行被注册函数

handler()

}

})

window.postMessage(‘‘) // node 中有prosee.nexTick()

this._finally(this.value)

}

4.实现promise链式调用如promise.then().then()以及实现

then(resolveHandler, rejectHandler) {

        // 实现连续的.then().then().then()调用,通过.then()返回新的promise供后续.then调用
        return new  KPromise((resolve, reject) =>{ // 外层包装了一层promise
            function newResolvedHandler(val) {
                if(typeof resolveHandler === ‘function‘) { // 判断第一个是否是函数
                    let result = resolveHandler(val) // 第一参数也就是前一个.then先执行

                    if(result instanceof KPromise) {
                        result.then(resolve, reject)
                    } else {
                        resolve(result)
                    }
                } else {
                    resolve(val)
                }

            }

            function newRejectHandler(val) {
                if (typeof rejectHandler === ‘function‘){
                    let result = rejectHandler(val) // 第二个参数就是前一个.then(,reject())第一reject先执行

                        if(result instanceof KPromise) {
                            result.then(resolve, reject)
                            } else {
                               reject(result)
                            }
                } else {
                    reject(val)
                }

            }

            this.resolveQueues.push(newResolvedHandler)

            this.rejectQueues.push(newRejectHandler)
        })

    }

上面所述都是个人认为有坑的地方:下面附上完整代码

class CPromise {
    // 定义三个状态常量
    static PENDING = ‘PENDING‘;
    static RESOLVED = ‘RESOLVED‘;
    static REJECTED = ‘REJECTED‘; 

    constructor( handler ) {

        if ( typeof handler !== ‘function‘ ) throw new TypeError(‘Promise resolver undefined is not a function‘);

        this.resolvedQueues = []; // 定义一个空数组,注册保存调用.then(resolve, )方法传递的第一个resolve函数
        this.rejectedQueues = []; // 定义一个空数组,注册保存调用.then(, reject)方法传递的二个reject函数
        this.finayllyQueues = []; // 定义一个空数组,注册保存调用.finally(finally)这里的finally函数,

        this.status = CPromise.PENDING;
        this.value;

        handler( this._resolve.bind(this), this._reject.bind(this) ); // 通过bind改变当前函数this执行,然后就可以使用this.status,RESOLVED等常量

    }

    _resolve(val) {
        // setTimeout(_=>{

        // }, 0);

        window.addEventListener(‘message‘, _=>{
            // message是h5的新特性,这里涉及到异步中的宏认为和微任务执行优先级,这里message事件属于微任务优先于setTimeout执行,例如下面的demo

            // setTimeout(() => {console.log(123)}, 0)
            // CPromise.resolve().then((res)=>{console.log(456)})
            // 上面代码先输出456然后输出123

            if (this.status !== CPromise.PENDING) return; // 这里的判断是防止当状态是resolved或者是rejected也就是状态被固定时状态被改变
            // console.log(‘_resolve‘);
            this.status = CPromise.RESOLVED; // 将状态置位resolve 并且不允许改变
            this.value = val;

            let handler;
            while( handler = this.resolvedQueues.shift() ) { // 执行以队列方式(先进先出方式)执行被注册函数
                handler(this.value); // 执行.then里面第一参数即resolve方法
            }
            this._finally(this.value); // 最后执行finally方法
        });
        window.postMessage(‘‘); // 触发上面的message事件
    }

    _reject(val) {
        // setTimeout(_=>{

        // }, 0);

        window.addEventListener(‘message‘, _=>{
            if (this.status !== CPromise.PENDING) return; // 这里的判断是防止当状态是resolved或者是rejected也就是状态被固定时状态被改变
            this.status = CPromise.REJECTED; // 将状态置位rejected 并且不允许改变
            this.value = val;

            let handler;
            while( handler = this.rejectedQueues.shift() ) { // 执行以队列方式(先进先出方式)执行被注册函数
                handler(this.value); // 调用.then里面第二个参数即rejected方法
            }
            this._finally(this.value); // 最后执行finally方法
        });
        window.postMessage(‘‘);
    }

    _finally() { // 通过此方法触发在finally方法注册的finayllyQueues数组里面的所有方法
        window.addEventListener(‘message‘, _=>{
            this.status = CPromise.REJECTED;

            let handler;
            while( handler = this.finayllyQueues.shift() ) { // 执行以队列方式(先进先出方式)执行被注册函数
                handler(this.value);
            }
        });
        window.postMessage(‘‘);
    }

    then( resolvedHandler, rejectedHandler ) {
        return new CPromise( (resolve, reject) => { // 这里返回以个promise是为了满足.then().then()链式调用
            function newResolvedHandler(val) {
                if (typeof resolvedHandler === ‘function‘) { // 如果then第一个参数是函数
                    let result = resolvedHandler(val);

                    if (result instanceof CPromise) { // 如果函数返回是一个promise
                        result.then(resolve, reject);
                    } else {
                        resolve(result);
                    }
                } else {
                    resolve(val);
                }
            }
            function newRejectedHandler(val) {
                if (typeof rejectedHandler === ‘function‘) { // 如果then第一个参数是函数
                    let result = rejectedHandler(val);
                    if (result instanceof CPromise) { // 如果函数返回是一个promise
                        result.then(resolve, reject);
                    } else {
                        reject(result);
                    }
                } else {
                    reject(val);
                }
            }

            this.resolvedQueues.push(newResolvedHandler); // push到相应数组等到resolve时遍历执行里面函数
            this.rejectedQueues.push(newRejectedHandler); // push到相应数组等到resolve时遍历执行里面函数

        } );
    }

    catch(rejectedHandler) {
        return this.then(undefined, rejectedHandler); // 这里的catch实现其实是调用了then方法来实现
    }

    finally(finallyHandler) {
        this.finayllyQueues.push(finallyHandler);
    }

    static resolve(val) { // 静态resolve方法 通过promise.resolve()方式调用
        return new CPromise(resolve => {
            resolve(val);
        });
    }

    static reject(val) { // 静态reject方法 通过promise.reject()方式调用
        return new CPromise((resolve, reject) => {
            reject(val);
        });
    }

    static all(iterator) {

        let len = iterator.length;
        let i = 0;
        let vals = [];

        return new CPromise( (resolve, reject) => {
            iterator.forEach(it => {
                it.then(val => {
                    i++;
                    vals.push(val);
                    if (i === len) {  // 当所有promise都被执行完了,才resolve结束
                        resolve(vals);
                    }
                }).catch(e=> {
                    reject(e);
                });
            });
        } );

    }

    static race(iterator) { // iterator 是一个promise数组
        return new CPromise((resolve, reject) => {
            iterator.forEach(it => {
                it.then(val => { // 返回封装成promise的数组,只要有一个被resolve就行
                    resolve(val);
                }).catch(e=> {
                    reject(e);
                });
            });
        })
    }

}

有不对的地方,希望评论指正,个人看了些东西然后写下的一点点理解希望能对你有所帮助!!!

原文地址:https://www.cnblogs.com/chengeping/p/11539935.html

时间: 2024-08-05 07:24:20

js手写一个实现一个简单的promise__小前端chenMr的相关文章

js手写'Promise'

/* * pending:初始化成功 * fulfilled:成功 * rejected:失败 * */ function Promise(executor) {// 执行器 this.status = 'pending'; this.value = undefined; this.reason = undefined; this.fulfilledCallback = []; this.rejectCallback = []; let resolve = (value)=>{ if(this.

轮播图--JS手写

轮播图基本每个网站都会有,也有很多的JQuery插件可以用,这里是用JS代码写的. @{ Layout = null; } <!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width" /> <title>Test</title> <script src="~/Scripts/jq

简单的 js手写轮播图

html: <div class="na1">   <div class="pp">    <div class="na">     <img class="dd" src="../img/shouji/1.jpg">    </div>    <div class="na">     <img class=&

浅谈时钟的生成(js手写代码)

在生成时钟的过程中自己想到布置表盘的写法由这么几种: 当然利用那种模式都可以实现,所以我们要用一个最好理解,代码有相对简便的方法实现 1.利用三角函数 用js在三角函数布置表盘的过程中有遇见到这种情况:是在表盘的刻度处,利用三角函数计算具体的值时不能得到整数,需要向上或者向下取整,这样无形中就会存在些许偏差,而且这样的偏差难利用样式来调整到位,即使最终效果都可以实现,但是细微处的缝隙和角度的偏差都会影响整体的视觉体验,作为一名程序开发人员,这样的视觉体验很难让别人认可,放弃. 2.利用遮罩层 j

js手写数组Api--模拟实现常见数组Api

数组的API经常用,但是api的内部实现还没研究过,于是就研究学习了下. 原文地址: https://www.cnblogs.com/yalong/p/11606865.html 数组的API的具体使用方看这里 API详细用法本文记录了数组中的 every,filter, find , indexOf, forEach, from, includes, isArray, map, reduce,slice,splice, sort这些个API的实现,如有不当,欢迎指出. Every 定义和用法:

js手写日历插件

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>日历</title> <style> table {

使用js手写兼容jquery的ajax

jquery-ajax.js var $ = new function() { this.ajax = function(param) { if(!param){return;} // compatible all new browsers and IE5 and IE6 var xmlhttp = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP"); if (xml

opencv实现KNN手写数字的识别

人工智能是当下很热门的话题,手写识别是一个典型的应用.为了进一步了解这个领域,我阅读了大量的论文,并借助opencv完成了对28x28的数字图片(预处理后的二值图像)的识别任务. 预处理一张图片: 首先采用opencv读取图片的构造函数读取灰度的图片,再采用大津法求出图片的二值化的阈值,并且将图片二值化. 1 int otsu(const IplImage* src_image) { 2 double sum = 0.0; 3 double w0 = 0.0; 4 double w1 = 0.0

原生JS取代一些JQuery方法的简单实现

原生JS取代一些JQuery方法的简单实现 下面小编就为大家带来一篇原生JS取代一些JQuery方法的简单实现.小编觉得挺不错的,现在就分享给大家,也给大家做个参考.一起跟随小编过来看看吧 1.选取元素 // jQuery var els = $('.el'); // Native var els = document.querySelectorAll('.el'); // Shorthand var $ = function (el) { return document.querySelect