Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

从一道promise题说说promise的实现 #6

Open
Diazhao opened this issue Mar 27, 2019 · 4 comments
Open

从一道promise题说说promise的实现 #6

Diazhao opened this issue Mar 27, 2019 · 4 comments

Comments

@Diazhao
Copy link
Owner

Diazhao commented Mar 27, 2019

先来看下这个题目

console.log(1);
new Promise( (resolve, reject) => {
    reject();
    setTimeout( () => {
        resolve();
    }, 0)
}).then(() => {
    console.log(2)
},() => {
    console.log(3)
});
console.log(4)
@Diazhao
Copy link
Owner Author

Diazhao commented Mar 27, 2019

初看这个题目,小子我心头淡然一笑,1,4,2,3 答案不假思索就脱口而出。这个题目不就是想考察我们js的Event Loop嘛。然后后面看到答案,瞬间感觉跳进了一个坑里。下面一个一个的拆解开来说

  1. new Promise()的时候,构造函数里面传入的这个函数的执行时机。
    这是一个比较常见的问题,作为异步问题的解决方案,Promise一般会在构造函数中接收一个异步函数参数,在定义了Promise内部的resolve与reject方法之后,会直接执行这个异步函数。Promise的构造函数大致如下所示
function Promise(excutor){
    var status = 'pending';
    
    this.onResolveCallback = [];
    this.onRejectCallback = [];

    function resolve(){
        if(status === 'pending'){
             status = 'resolved';
             this.onResolveCallback.forEach((callback) => {
                  callback();
             })
        }
    }
    function reject(){
        if(status === 'pending'){
             status = 'reject';
             this.onResolveCallback.forEach((callback) => {
                  callback();
             })
        }
    }

    try {
        excutor()
    }catch (e) {
        reject()
    }
}

根据上面的代码可以看出来,excutor确实是new的同时就会执行了的。
excutor的执行没有绑定任何this,因此回到题目中,这我们new Promise(function(){console.log(this)})的时候,这个this的指向,的确是我们执行函数这里的this指向,这题目中就是window,与Promise没有关系。这个问题又涉及到js的词法作用域,后面的问题会说到。
既然在new的时候就会直接执行了我们传入的函数,那么我的第二个问题又来了,这时候reject和resolve的数组中还是空的,自然不会执行后面console.log的东西,为什么还是会输出呢,也就是第二个问题,

  1. then函数的执行时机?
    我们先看下then在Promise上的实现
Promise.prototype.then = function(onResolve,onReject){
    var self = this;
    var promise2,
        onResolve = typeof onResolve === 'function' ? onResolve : function(value){},
        onReject = typeof onReject === 'function' ? onReject : function(){reject};

    if(self.status === 'resolved'){
        return promise2 = new Promise(function(resolve,reject){
            try{
                var x = onResolve(self.data);
                if(x instanceof Promise){
                    x.then(resolve,reject)
                }
                resolve(x);
            }catch (e){
                reject(e);
            }
        })
    }
    if(self.status === 'rejected'){
        return promise2 = new Promise(function(resolve,reject){
            try{
                var x = onReject(self.data);
                if(x instanceof Promise){
                    x.then(resolve,reject)
                }
                resolve(x);
            }catch (e){
                reject(e);
            }
        })
    }
    if(self.status === 'pending'){
        return promise2 = new Promise(function(resolve,reject){
            self.onResolveCallback.push(function(value){
                try{
                    var x = onResolve(self.data);
                    if(x instanceof Promise){
                        x.then(resolve,reject)
                    }
                    resolve(x);
                }catch (e){
                    reject(e);
                }
            })
            self.onRejectCallback.push(function(value){
                try{
                    var x = onReject(self.data);
                    if(x instanceof Promise){
                        x.then(resolve,reject)
                    }
                    resolve(x);
                }catch (e){
                    reject(e);
                }
            })
        })
    }
}

看完了代码,回到原有的题目上,在new Promise的时候,直接执行了reject的逻辑,此时这个Promise的状态为reject,因此在then的时候,会执行如下这段代码

    if(self.status === 'rejected'){
        return promise2 = new Promise(function(resolve,reject){
            try{
                var x = onReject(self.data);
                if(x instanceof Promise){
                    x.then(resolve,reject)
                }
                resolve(x);
            }catch (e){
                reject(e);
            }
        })
    }

所以,就得到了我们的最终答案,1,4,3

  1. 总结
    这个题目其实很简单,但是总会‘坑’到人。不仔细看的话,还是很容易答错的。
    其目的其实也很简单,主要就是考察Promise的用法,其实与js执行Event Loop没什么大的关系。
    如果想要考察js执行顺序的话,题目可以这样延伸下。下面这个题目又会输出什么呢?
console.log(1);
new Promise( (resolve, reject) => {
    setTimeout( () => {
        resolve();
    }, 0);
    reject();
}).then(() => {
    console.log(2)
},() => {
    console.log(3)
});
setTimeout(() => {console.log(4)})

参考:
剖析Promise内部结构,一步一步实现一个完整的、能通过所有Test case的Promise类

@ab164287643
Copy link

状态从pending变成reject就直接进入then里面的onReject回调了 1,4,3

@Diazhao
Copy link
Owner Author

Diazhao commented May 5, 2019

状态从pending变成reject就直接进入then里面的onReject回调了 1,4,3

是的,但是上个月所有的面试者几乎都答错了。。。

@EPSON-LEE
Copy link

我觉得这道题目和 Promise 的实现好像没什么太大关系

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants