Promise的出现与实现

这篇文章总结一下JS本身单线程异步的局限性(promise出现的原因),以及实现一个简易的Promise。

单线程与异步

  JavaScript是一个单线程执行的语言,在不考虑异步编程的情况下,它执行的顺序就是一个eventLoop的简单循环。比如书写一段简单的JS代码:

// 声明两个变量,和一个函数
var demoVariA = 100;
var demoVariB = 200;
// 函数的功能是把入参的两个数值相加
function addTowNum (a, b) {
    return a + b;
}
addTowNum(demoVariA, demoVariB);

  那么在不考虑预解析的情况下(变量函数作用域提升),我们试着把上面这段代码执行顺序用一个eventLoop来简单表示出来。

// 代码的执行队列
eventLoop = [
    '赋值100给内存demoVariA',
    '赋值200给内存demoVariB',
    '申请堆栈内存声明为addTowNum,赋值为一个函数',
    '执行函数'
];
// 现在JS代码需要分析执行
// 这里不考虑任何异步的情况,包括宏任务和微任务
while (eventLoop.length > 0) {
    // 这里表示对词法token进行编译借的过程的抽象
    var event = eventLoop.shift();
    try {
        event();
    } catch (error) {
        report(error);
    }
}

  我们可以看到,不需要考虑多线程共享数据时线程执行先后对程序造成的影响,不需要使用进程锁的概念,词法的执行顺序是多么的清晰,单线程编程是多么的令人愉快。直到异步进入到我们的编程世界。

  同样是上面的例子,我们把demoVariA,demoVariB的数据请求改为异步的请求获取值asyncGetSomeValue,事情就完全不一样了。

var demoVariA = asyncGetSomeValue('pathA');
var demoVariB = asyncGetSomeValue('pathB');
function addTowNum (a, b) {
    return a + b;
}
addTowNum(demoVariA, demoVariB); // undefined  + undefined = NaN

  asyncGetSomeValue必须在某个异步操作之后,再获取到值,因此按照JS的常规做法,我们必须把demoVariA,demoVariB放到回调当中去获取值,我们把代码修改如下:

var demoVariA;
var demoVariB;
asyncGetSomeValue('pathA', function (responseValue) {
    demoVariA = responseValue;
});
asyncGetSomeValue('pathB', function (responseValue) {
    demoVariB = responseValue;
});
function addTowNum (a, b) {
    return a + b;
}

  现在问题来了addTowNum,该放到哪里去执行才能a和b同时获取到呢。有的同学此时会说,简单,我把demoVariB获取放到demoVariA获取的回调下,再去执行addTowNum

var demoVariA;
var demoVariB;
asyncGetSomeValue('pathA', function (responseValue) {
    demoVariA = responseValue;
    asyncGetSomeValue('pathB', function (responseValue) {
        demoVariB = responseValue;
        addTowNum(demoVariA, demoVariB);
    });
});

function addTowNum (a, b) {
    return a + b;
}

  实际上,demoVariB的请求并没有依赖到demoVariA,因此把demoVariB的取值请求放到demoVariA的后面的做法是错误的,这样会导致addTowNum这个函数的调用时间将会变成两个异步请求费时的总和。这两个获取值得请求严格意义上应该是并发的概念。我们要做的是需要声明一个为gate的函数,当你传入的所有变量存在的时候,去执行传入的函数。

/**
 * @param {function} gateFunction 所有变量存在后执行的函数
 * @param 剩余变量
  */
function gate (gateFunction) {
    var testArray = arguments.slice(1);
    // 全部变量都存在的时候,才执行gateFunction
    if (testArray.every(variable => variable)) {
        gateFunction.apply(this, testArray);
    }
}

  此时加入gate函数,我们上面的修改为异步的例子才算简单完成。实际业务编程当中,我们还要考虑asyncGetSomeValue的异常抛错等问题。

var demoVariA;
var demoVariB;
asyncGetSomeValue('pathA', function (responseValue) {
    demoVariA = responseValue;
    gate(addTowNum, a, b);
});
asyncGetSomeValue('pathB', function (responseValue) {
    demoVariB = responseValue;
    gate(addTowNum, a, b);
});
function addTowNum (a, b) {
    return a + b;
}

  通过这个简单的例子我们不难发现异步编程的问题:

  1. 多异步返回的执行顺序不可控。
  2. 多异步的异常错误处理非常繁杂。
  3. 多异步嵌套,会导致回调地狱。

  我们急需要一个能够保证异步执行顺序,保证执行和抛出错误的异步处理的保证范式来解决这些问题。ES6给我们的答案就是Promise(承诺)。

Promise的特性

都是将来

  Promise的使用方法不做阐述,想要了解的可以去查询一下MDN

  Promise的回调than有一个非常重要的特性,那就是无论是现在还是将来,统一都是将来。我们来猜一下下面代码的执行顺序:

var promise = new Promise(function (resolve, reject) {
   
    console.log(1);
    setTimeout(() => {
   
        console.log(2);
    }, 0)
    resolve(3);
});
promise.then(val => console.log(val))<
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值