普通视图

发现新文章,点击刷新页面。
今天 — 2026年1月12日首页

Promise为什么比回调函数更好

2026年1月11日 19:45

Promise是什么

promise是什么, promise是一种为你等待底层异步值完成的机制,同时promise也是一种异步任务流程控制的机制,以下举例说明这两种理解

Promise等待了未来值

我们先看一个例子, 假设我们有两个函数 fetchX, fetchY,我们需要把这两个函数的结果相加

let x, y;
fetchX(function (x) {
    x = x;
});
fetchY(function (Y) {
    y = y;
});

console.log(x + y);

在上述中,console.log(x + y);的结果是不确定的,当x已经决议(完成),但是y还没有决议,或者是y已经决议,但是x还没有决议,又或者是两者都没有决议,那么我们就完全得不到想要的结果,怎么去解决这个问题呢,我们可以写一个函数去解决这个问题

function add (fetchX, fetchY, cb) {
    let x, y;
    fetchX(function (valX) {
        x = valX;
        
        if (y !== undefined) { //y准备好了?
            cb(x + y);
        }
    });
    
    fetchY(function (valY) {
        y = valY;
        
        if (x !== undefined) {//x准备好了?
            cb(x + y);
        }
    });
    
}

//调用add
add(fetchX, fetchY, function (sum) {
    console.log(sum);
})

在这段代码中,我们使用了一个函数add,这个函数它为我们等待了x或者y决议,并且在全部完成决议的时刻执行打印,这个函数把现在和将来的结果都统一了,它不在会有undefined的成分了,同时你可以发现,我们把console.log的代码放到了将来执行,也就是说,这个函数把现在的代码(指打印操作)和将来的代码(指赋值操作),全部放到了将来,这个也就是promise的概念,请看以下代码

function add (xPromise, yPromise) {
    return Promise.all([xPromise, yPromise])
      .then((value) => {
        return value[0] + value[1];
    })
}

add(fetchX, fetchY)
  .then((sum) => {
    console.log(sum);
}, (err) => {
    console.error(err);
})

这段函数和上述的函数结果是一样的,区别在于上述的代码非常丑陋,所以我们可以说promise是一种为你等待未来值并且把代码全部变成异步的机制

Promise控制异步任务流程

请看以下代码

function foo (x) {
    return new Promise((resolve, reject) => {
        //最终可能调用resolve(...)或者reject(...)
    })
}

function bar () {
    //foo完成,执行bar的任务
}

function oopsBar () {
    //foo执行出错
}

function baz () {
    //foo完成,执行baz的任务
}
function oopsBar() {
    //foo执行出错
}

var p = foo(42);

p.then(bar, oopsBar);
p.then(baz, oopsBaz);

在上述代码中,当p执行成功。则会执行函数bar, baz,如果出错,则执行函数oopsBar,oopsBaz,从这个意义上说,foo在成功的时候才会调用bar, baz,出错则调用oppsBar,oppsBaz,不管是什么结果,都是通过Promise控制接下来的流程,,从这个角度上我们说promise是一种控制异步任务流程的机制

Promise为什么解决了回调函数的问题

回调函数在异步流程中的两个问题,一个是回调函数在异步编程中非线性非顺序,一个是回调函数在异步编程中的信任问题。首先,promise写法是线性的,每一个then都不需要关心上一个then的时间状态,当then的回调执行的时候,上一个then的promise必然是已经决议了,这就解决了嵌套带来的问题。 其次是回调函数的信任问题。

调用过早

调用过早主要是指回调函数是否是有时同步有时异步,回调函数如果有时候是同步,有时候又是异步,这样的问题被称为Zalgo,但是promise天生就避免了这个问题,Promise的then总是会被异步调用,即使你的给Promise传入的函数是立即决议的,类似于new Promise(function () { resolve(42) } ), 它的then也会被异步的调用

调用过晚

Promise的对象在完成的时候(Promise对象调用resolve或reject),这个promise的then注册的回调函数就在下一个异步事件点上一定会被触发, 请你判断下面的例子的顺序

p.then(function () {
   p.then(funcion () {
          console.log("C");
        });
    console.log("A");
});
p.then(function () {
   console.log("B"); 
});

在这个例子中, C无法打断或者抢占B, 执行顺序一定是B,Promise这样的运行方式保证了B不会被后注册的回调函数执行打印了C导致从调用过晚

回调未调用

Promise的then一定会被调用,这是promise机制本身保证的,如果promise永远没有被完成,也可以使用代码解决

function timeoutPromise(delay) {
    return new Promise(function (resolve, reject) {
        setTimeout(function () {
            reject("Timeout");
        }, delay)
    })
}


Promise.race([
    foo(),
    timeoutPromise(3000)
]).then(function () {
    //foo..及时完成
}, function () {
    //foo拒绝或者未按时完成
})

总结

Promise为什么比回调函数好,一点在于可读性,另一点在于Promise把回调函数的控制反转再反转,通过Promise去执行我们的回调,所以Promise解决信任问题的方法可以说是把控制权给了一个绝对可信任的系统(Promise),而不是第三方库

❌
❌