Promise的适用场景

一.实现串行任务队列

任务队列是指A完成了调用B,B完成了调用C(当然,这里的ABC都表示异步操作)…以前的做法是嵌套回调,代码类似于:

taskA(data1, function(res) {
    if (res.state === OK) {
        taskB(data2, function(res) {
            if (res.state === OK) {
                taskC(data3, function(res) {
                    if (res.state === OK) {
                        // ...
                    }
                    else {
                        // err3
                    }
                });
            }
            else {
                // err2
            }
        });
    }
    else {
        // err1
    }
});

其间每一步都可能出错(err1, err2, err3…),错误处理也可能不同,代码结构会变得非常臃肿(不可控制地“横向发展”,最终变成“一坨”代码)。因为每一步都是异步 + 回调,所以很难把各个callback分离开

Promise最明显的效果就是消除了这种“回调金字塔”,代码不会再横向发展:

// taskX都返回Promise对象
taskA(data1).then(
    taskB(data2),
    errHandler1
).then(
    taskC(data3),
    errHandler2
).then(
    displayData,
    errHandler3
);

如果taskA和taskB的错误处理相同,还可以更简单:

// taskX都返回Promise对象
taskA(data1).then(taskB(data2)).then(
    taskC(data3),
    errHandler12
).then(
    displayData,
    errHandler3
);

如果想统一处理错误,可以这样做:

// taskX都返回Promise对象
taskA(data1).then(taskB(data2)).then(taskC(data3)).then(displayData).catch(errHandler);
// 等价于
taskA(data1).then(taskB(data2)).then(taskC(data3)).then(displayData, errHandler);

链式调用 + 错误自动后抛,写出来好看,读起来也不错

二.实现串行任务管道

任务管道是指当前任务的输出可以作为下一个任务的输入,形成一条数据管道,jQuery代码类似于:

$.post(url1, data, function(res) {
    if (res.state === OK) {
        $.post(url2, res.data, function(res) {
            if (res.state === OK) {
                $.post(url3, res.data, function(res) {
                    if (res.state === OK) {
                        // ...
                    }
                    else {
                        // err3
                    }
                });
            }
            else {
                // err2
            }
        });
    }
    else {
        // err1
    }
});

实际场景很常见,比如从url1获取参数userId,拿到后再从url2换取第三方openId,最后再从url3换取orderList,然后把结果展示给用户,类似的逻辑都是天然的任务管道

用Promise可以这样实现:

new Promise(function(resolve, reject) {
    resolve(1);
}).then(function(res) {
    return new Promise(function(resolve, reject) {
        resolve(res + 1);
    });
}).then(function(res) {
    return new Promise(function(resolve, reject) {
        resolve(res + 1);
    });
}).then(function(res) {
    console.log(res);
});
// => 3

P.S.jQuery的$.when()就是一种Promise实现,但与Promise A+规范有些差异,详细请查看jQuery deffered和promise对象方法

三.处理运行时才能确定执行顺序的串行任务

假设有A,B,C3个任务,执行顺序不确定,有可能是BCA、BAC等等,运行时才能确定3者的执行顺序,一旦确定顺序,就要立即串行执行

此时回调嵌套已经无法满足了,因为每一种顺序都要对应一种回调嵌套,我们需要一种能够随意组合回调的机制,比如Promise:

var tasks = getOrderedTasks();
var p = tasks[0];
tasks.forEach(function(item, index) {
    p.then(item);
});

这样写的副作用是管道没了(任务的输出无法传递下去作为下一个任务的输入),不过没关系,很容易修改:

function catchFuture(future) {
    // 处理/传递future
    // ...
    console.log('catchFuture: ' + future);
}
var tasks = getOrderedTasks();
var p = tasks[0];
tasks.forEach(function(item, index) {
    // p.then(item);
    p.then(item.then(catchFuture));
});

发表评论

电子邮件地址不会被公开。 必填项已用*标注

*

code