// 创造 Promise 实例
//Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolve和reject。
var promise = new Promise(function(resolve,reject){
//... some code
if( /*异步操作成功*/ ){
resolve('success') ; //可以是任意值,字符串、对象,甚至是另一个Promise对象
}else{
reject('error')
}
})
//使用 Promise 实例
//then方法分别指定Resolved状态和Reject状态的回调函数。
promise.then(function(value){
//...
},function(error){
//...
})
function timeout(ms){
return new Promise(resolve,reject){
setTimeout(resolve,ms,'done');
}
}
timeout(2000).then((v)=>{
console.log(v); //2000ms 后打出 done
})
function loadImage(url){
return new Promise( function(resolve,reject){
var image = new Image();
image.src = url;
image.onload = function(){
resolve(image)
}
image.onerror = function(){
reject('error')
}
})
}
loadImage('http://p3.qhimg.com/d/inn/d0d222c6/b.png')
.then((v)=>{
console.log(v)
})
- reject函数的参数通常是 Error 对象的实例
- resolve函数的参数除了正常值外,还可以是另一个Promise实例(表示这个异步操作的结果可能是一个值,也可能是另一个异步操作)
Promise 实例具有 then 方法,作用是为 Promise 实例添加状态改变时的回调函数。
有两个参数:resolve回调函数,reject回调函数
可以链式调用,前一个回调函数可能返回另一个Promise对象,这时后一个回调函数,会等待该Promise对象状态发生变化,才会被调用。
getJSON("/post/1.json").then(function(post) {
return getJSON(post.commentURL);
}).then(function funcA(comments) {
console.log("Resolved: ", comments);
}, function funcB(err){
console.log("Rejected: ", err);
});
上面代码中,第一个then方法指定的回调函数,返回的是另一个Promise对象。这时,第二个then方法指定的回调函数,就会等待这个新的Promise对象状态发生变化。如果变为Resolved,就调用funcA,如果状态变为Rejected,就调用funcB。
下面一共三个Promise对象,一个由getJSON产生,两个由then产生,它们之中任何一个抛出错误,都会被最后一个 catch 捕获。
p.then(function(){
return getJSON('xx')
}).then(function(){
...
}).catch(function(){
...
})
Promise.all 将多个 Promise 实例包装成一个新的 Promise 实例
接受一个数组作为参数
- 所有的 Promise 实例状态都变为 fulfilled 时, all 状态变为 fulfilled
- 只要有一个 Promise 实例被 rejected,all 状态变为 rejected
function readFile(file){
return new Promise(function(resolve,reject){
fs.readFile(file,function(err,data){
if(err){
reject(err)
}else{
resolve(data)
}
})
})
}
var p1 = readFile('test3.js')
var p2 = readFile('test2.js')
var p = Promise.all([p1,p2]);
p.then(function(v){
console.log(v); //将2个文件的内容包在数组中输出 [<Buffer>,<Buffer>]
})
同 Promise.all
,但只要 Promise 实例数组中有一个状态被改变,race 状态就改变
将现有对象转为 Promise 对象
Promise.resolve('foo')
//等价于
new Promise(function(resolve){
resolve('foo')
});
同 Promise.resolve
Generator函数是一个状态机,封装了多个内部状态。执行Generator函数会返回一个遍历对象,可以一次遍历Generator函数内部的每一个状态。
每次调用遍历器对象的next方法,就会返回一个有着value和done两个属性的对象。value属性表示当前的内部状态的值,是yield语句后面那个表达式的值;done属性是一个布尔值,表示是否遍历结束。
function* helloGenerator(){
yield 'hello';
yield 'world';
yield 'end';
}
var hg = helloGenerator()
hg.next(); //{ value: 'hello', done: false }
hg.next(); //{ value: 'world', done: false }
hg.next(); //{ value: 'end', done: true }
hg.next(); //{ value: undefined, done: true }
和普通函数不同的是,调用Generator函数后,该函数并不执行,返回的也不是函数运行结果,而是一个指向内部状态的指针对象(Iterator Object)。
必须调用遍历器对象的next方法,使得指针移向下一个状态。调用next方法,内部指针就从函数头部或上一次停下来的地方开始执行,直到遇到下一个yield语句(或return语句)为止。换言之,Generator函数是分段执行的,yield语句是暂停执行的标记,而next方法可以恢复执行。
yield是暂停标志,后面的表达式只有当调用 next
方法时才会执行。
yield没有返回值(总返回undefined),next方法可以带一个参数,该参数被当做上一个yield语句的返回值。
function* f() {
for(var i=0; true; i++) {
var reset = yield i;
if(reset) { i = -1; }
}
}
var g = f();
g.next() // { value: 0, done: false }
g.next() // { value: 1, done: false }
g.next(true) // { value: 0, done: false }
如果next方法没有参数,每次运行到yield语句,变量reset的值总是undefined。当next方法带一个参数true时,当前的变量reset就被重置为这个参数(即true),因此i会等于-1,下一轮循环就会从-1开始递增。
function* foo(x) {
var y = 2 * (yield (x + 1));
var z = yield (y / 3);
return (x + y + z);
}
var a = foo(5);
a.next() // Object{value:6, done:false}
a.next() // Object{value:NaN, done:false} y = 2 * undefined ,因为 yield 返回值是 undefined (除非外部给 next() 传值)
a.next() // Object{value:NaN, done:true}
var b = foo(5);
b.next() // { value:6, done:false }
b.next(12) // { value:8, done:false } yield 返回12 , y = 2 * 12 , z = 24 / 3
b.next(13) // { value:42, done:true } yield 返回13 , z = 13 ,x + y + z = 5 + 24 + 13 = 42
这个功能有很重要的语法意义。Generator函数从暂停状态到恢复运行,它的上下文状态(context)是不变的。通过next方法的参数,就有办法在Generator函数开始运行之后,继续向函数体内部注入值。也就是说,可以在Generator函数运行的不同阶段,从外部向内部注入不同的值,从而调整函数行为。
function* dataConsumer() {
console.log('Started');
console.log(`1. ${yield}`);
console.log(`2. ${yield}`);
return 'result';
}
let genObj = dataConsumer();
genObj.next(); // Started
genObj.next('a') // 1. a
genObj.next('b') // 2. b
返回给定的值,并且终结遍历Generator函数。
function readFile(name){
return new Promise(function(resolve,reject){
fs.readFile(name,function(err,data){
if(err){
reject(error)
}
resolve(data)
})
})
}
function* gen(){
var f1 = yield readFile('index.js')
var f2 = yield readFile('test2.js')
console.log(f1)
console.log(f2)
}
var g = gen();
g.next().value.then(function(data){
g.next(data).value.then(function(data){
g.next(data);
});
});
g.next().value
是一个 Promise 对象
co模块用于 Generator 函数自动执行
function readFile(name){
return new Promise(function(resolve,reject){
fs.readFile(name,function(err,data){
if(err){
reject(error)
}
resolve(data)
})
})
}
co(function* (){
var f1 = yield readFile('index.js')
var f2 = yield readFile('test2.js')
console.log(f1)
console.log(f2)
})
async function asyncReadFile(){
var f1 = await readFile('index.js')
var f2 = await readFile('test2.js')
console.log(f1)
console.log(f2)
}
asyncReadFile()
- 内置执行器,Generator没有,所以才有 co
- async/await 对比 * 和 yield 更好理解
- yield 和 await 后面都是 Promise 对象
- 返回值是 Promise, Generator函数返回值是 Iterator 对象
- async 函数返回一个Promise对象
async function f(){
return 'a';
}
f().then((v)=>{
console.log(v); // a
})
async function a(){
throw new Error('error')
}
a().then((v)=>{
console.log(v)
},(e)=>{
console.log(e) // Error: error
})
-
async 函数返回的Promise对象,必须等到内部所有 await 命令的Promise对象执行完,才会发生状态改变。即,只有 async 函数内部的异步操作执行完,才会执行 then 方法指定的回调函数。
-
await 命令后面应该是一个 Promise 对象,如果不是,会被转成一个立即 resolve 的 Promise 对象。
-
只要一个 await 后面的 Promise 变为 reject ,那么整个 async 函数都会中断执行
-
await 后面的异步操作错误,那么等同于 async 函数返回的 Promise 对象被 reject
async 函数执行的时候,一旦遇到 await 就会等待,等到 await 后的异步操作完成,再接着执行后面的语句。
function timeout(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
async function asyncPrint(value, ms) {
await timeout(ms);
console.log(value)
}
asyncPrint('hello world', 50);
50ms 后 才会 console.log
async function myFunction() {
try {
await somethingThatReturnsAPromise();
} catch (err) {
console.log(err);
}
}
// 另一种写法
async function myFunction() {
await somethingThatReturnsAPromise()
.catch(function (err) {
console.log(err);
};
}
let foo = await getFoo();
let bar = await getBar();
上面代码中,getFoo和getBar是两个独立的异步操作(即互不依赖),被写成继发关系。这样比较耗时,因为只有getFoo完成以后,才会执行getBar,完全可以让它们同时触发。
// 写法一
let [foo, bar] = await Promise.all([getFoo(), getBar()]);
// 写法二
let fooPromise = getFoo();
let barPromise = getBar();
let foo = await fooPromise;
let bar = await barPromise;
Promise 语义不明显 Generator 需要执行器 async 最好
const f = (txt) => {
return new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve(txt)
},2000)
})
}
一个定时器做Promise对象
//Promise
f('axs')
.then(function(v){
console.log(v)
return f('zxc')
})
.then(function(v){
console.log(v)
})
// Generator
function* gen(){
var t1 = yield f('asd')
console.log(t1)
var t2 = yield f('xzc')
console.log(t2)
}
var g = gen()
g.next().value.then(function(data){
g.next(data).value.then(function(data){
g.next(data)
})
})
//co
co(function* (){
var t1 = yield f('asd')
console.log(t1)
var t2 = yield f('xzc')
console.log(t2)
})
//async/await
const test = async () => {
const t1 = await f('asd');
console.log(t1)
const t2 = await f('xzc')
console.log(t2)
}
test();
今天又发现一个 async/await 、Promise 与 generator 的一个大区别:只有 generator 能够在外层得到值,即 async/await 和 Promise 只能在 then 中得到值