原文一. 🔗
// 我们假设step1, step2, step3都是ajax调用后端
// 需求是这样,step1、step2、step3必须按顺序执行
function step1(resolve, reject) {
console.log('步骤1');
resolve('步骤11');
}
function step2(resolve, reject) {
console.log('步骤2');
resolve('步骤22');
}
function step3(resolve, reject) {
console.log('步骤3');
resolve('步骤33');
}
new Promise(step1).then(function(val){
console.log(val);
return new Promise(step2);
}).then(function(val){
console.log(val);
return new Promise(step3);
}).then(function(val){
console.log(val);
return val;
});
原文二. 🔗
首先是返回普通变量的情况:
new Promise(function(res, rej) {
console.log(Date.now() + " start setTimeout 1");
setTimeout(res, 2000);
}).then(function() {
console.log(Date.now() + " timeout 1 call back");
return 1024;
}).then(function(arg) {
console.log(Date.now() + " last onFulfilled return " + arg);
});
以上代码执行结果为:
$ node promisTest.js
1450277122125 start setTimeout 1
1450277124129 timeout 1 call back
1450277124129 last onFulfilled return 1024
是onFulfilled函数返回一个Promise变量可以使我们很方便的连续调用多个异步过程。比如我们可以这样来尝试连续做两个延时操作:
new Promise(function(res, rej) {
console.log(Date.now() + " start setTimeout 1");
setTimeout(res, 2000);
}).then(function() {
console.log(Date.now() + " timeout 1 call back");
return new Promise(function(res, rej) {
console.log(Date.now() + " start setTimeout 2");
setTimeout(res, 3000);
});
}).then(function() {
console.log(Date.now() + " timeout 2 call back");
});
执行结果如下:
$ node promisTest.js
1450277510275 start setTimeout 1
1450277512276 timeout 1 call back
1450277512276 start setTimeout 2
1450277515327 timeout 2 call back
可以看到,多个延时的回调函数被有序的排列下来,并没有出现喜闻乐见的金字塔状结构。虽然代码里面调用的都是异步过程,但是看起来就像是全部由同步过程构成的一样。这就是Promise带给我们的好处。
如果你有把啰嗦的代码提炼成单独函数的好习惯,那就更加画美不看了:
function timeout1() {
return new Promise(function(res, rej) {
console.log(Date.now() + " start timeout1");
setTimeout(res, 2000);
});
}
function timeout2() {
return new Promise(function(res, rej) {
console.log(Date.now() + " start timeout2");
setTimeout(res, 3000);
});
}
function timeout3() {
return new Promise(function(res, rej) {
console.log(Date.now() + " start timeout3");
setTimeout(res, 4000);
});
}
function timeout4() {
return new Promise(function(res, rej) {
console.log(Date.now() + " start timeout4");
setTimeout(res, 5000);
});
}
timeout1()
.then(timeout2)
.then(timeout3)
.then(timeout4)
.then(function() {
console.log(Date.now() + " timout4 callback");
});
$ node promisTest.js
1450278983342 start timeout1
1450278985343 start timeout2
1450278988351 start timeout3
1450278992356 start timeout4
1450278997370 timout4 callback
接下来我们可以再继续研究一下onFulfilled函数传入入参的问题。
我们已经知道,如果上一个onFulfilled函数返回了一个普通的值,那么这个值为作为这个onFulfilled函数的入参;那么如果上一个onFulfilled返回了一个Promise变量,这个onFulfilled的入参又来自哪里?
答案是,这个onFulfilled函数的入参,是上一个Promise中调用resolve函数时传入的值。
我们来看看普通的异步接口中,成功情况的回调是什么样的,就拿nodejs的上的fs.readFile(file[, options], callback)
来说,它的典型调用例子如下
fs.readFile('/etc/passwd', function (err, data) {
if (err) throw err;
console.log(data);
});
因为对于fs.readFile这个函数而言,无论成功还是失败,它都会调用callback这个回调函数,所以这个回调接受两个入参,即失败时的异常描述err和成功时的返回结果data。
那么假如我们用Promise来重构这个读取文件的例子,我们应该怎么写呢?
首先是封装fs.readFile函数:
function readFile(fileName) {
return new Promise(function(resolve, reject) {
fs.readFile(fileName, function (err, data) {
if (err) {
reject(err);
} else {
resolve(data);
}
});
});
}
其次是调用:
readFile('theFile.txt').then(
function(data) {
console.log(data);
},
function(err) {
throw err;
}
);
总结
下面请允许我用一段代码对本文讲解到的要点进行总结:
function callp1() {
console.log(Date.now() + " start callp1");
return new Promise(function(res, rej) {
setTimeout(res, 2000);
});
}
function callp2() {
console.log(Date.now() + " start callp2");
return new Promise(function(res, rej) {
setTimeout(function() {
res({arg1: 4, arg2: "arg2 value"});
}, 3000);
});
}
function callp3(arg) {
console.log(Date.now() + " start callp3 with arg = " + arg);
return new Promise(function(res, rej) {
setTimeout(function() {
res("callp3");
}, arg * 1000);
});
}
callp1().then(function() {
console.log(Date.now() + " callp1 return");
return callp2();
}).then(function(ret) {
console.log(Date.now() + " callp2 return with ret value = " + JSON.stringify(ret));
return callp3(ret.arg1);
}).then(function(ret) {
console.log(Date.now() + " callp3 return with ret value = " + ret);
});
$ node promisTest.js
1450191479575 start callp1
1450191481597 callp1 return
1450191481599 start callp2
1450191484605 callp2 return with ret value = {"arg1":4,"arg2":"arg2 value"}
1450191484605 start callp3 with arg = 4
1450191488610 callp3 return with ret value = callp3