Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

es6-Promise与异步编程 #21

Open
wozien opened this issue Oct 19, 2020 · 0 comments
Open

es6-Promise与异步编程 #21

wozien opened this issue Oct 19, 2020 · 0 comments
Labels

Comments

@wozien
Copy link
Owner

wozien commented Oct 19, 2020

Promise 是 es6 引入的异步处理方案,让我们可以采用链式的写法注册回调函数,摆脱多层异步回调函数嵌套的情况,使代码更加简洁。

基本用法

new Promise() 创建一个 Promise 对象:

let p = new Promise((resolve, reject) => {
  //
})

Promise 对象有3种状态:

  • pending: 表示进行中的状态
  • fulfilled: 任务完成的状态,调用resolve()后触发
  • rejected: 任务失败的状态, 调用rejected()后触发

状态只能从 pendingfulfilled,或者 pedingrejected。并且状态一旦发生改变,将不会恢复。

Promise对象的 then() 方法,第一个参数为状态变成fulfilled的处理函数,第二个为 rejected 的处理函数。处理函数的参数通过 resolve()方法或者 reject()方法的参数传递:

let p = new Promise((resolve, reject) => {
  // 修改promise对象的状态为fulfilled
  resolve(1);
});

p.then(v => {
  console.log(v);   // 1
});

catch() 方法同样可以捕获失败状态的 Promise 对象,所以下面两种写法等价:

let p = new Promise((resolve, reject) => {
  // 修改promise对象的状态为rejected
  reject(new Error('boom'));
});

p.then(null, err => {
  console.log(err.message);  // boom
});

// 等价于

p.catch(err => {
  console.log(err.message); // boom
});

Promise 初始化函数中抛出错误也是变成 rejected 状态:

let p = new Promise((resolve, reject) => {
  throw new Error('boom');
});

p.catch(err => {
  console.log(err.message); // boom
});

对于未处理的错误,catch() 总是能捕捉到。比如上面可以改写成:

let p = new Promise((resolve, reject) => {
  throw new Error('boom');
});

p.then(null).catch(err => {
  console.log(err.message); // boom
});

立即完成的Promise

Promise.resolve() 方法只接收一个参数并返回一个完成态的 Promise

let p = Promise.resolve(1);
p.then(v => console.log(v)); // 1

// 等价于
let p = new Promise((resolve, reject) => {
  resolve(1)
})

如果方法传入的是一个非Promise的 thenable 对象,指的是拥有 then()方法并接收 resolvereject 两个参数的普通对象。结果会返回一个新的Promise,并且执行thenable 中的then方法:

let thenable = {
  then(resolve, reject) {
    resolve(44);
  }
};

let p = Promise.resolve(thenable);

p.then(v => console.log(v)); // 44

如果传入的是一个Promise 对象,会原封不动的返回这个对象。

另外,通过 Promise.resolve() 创建的对象的 then() 方法执行是在本次事件循环:

setTimeout(() => {
  console.log('next event loop');
} 0);

let p = Promise.resolve('current event loop');

p.then(v => console.log(v));

// current event loop
// next event loop

利用 Promise.reject()可以创建立即失败的Promise,参数用法和上面类似。

串行的Promise

其实每次调用then()catch() 方法都会返回一个Promise对象,因此我们可以链式的调用:

let p = Promise.resolve(42);

p.then(v => console.log(v)).then(() => console.log('finish'));  // 42 finish

then() 或者 catch() 方法中抛出错误,会被下一个catch()方法捕获。所以我们推荐在链式的Promise最后一个为 catch():

let p = Promise.resolve(42);

p.then(v => {
  console.log(v);   // 42
  throw new Error('boom');
}).catch(e => {
  console.log(e.message);  // boom
});

then() 方法中可以return一个值,相当于该值会先作为 Promise.resolve() 参数调用,然后返回一个 Promise 对象,后续的方法调用取决与这个 Promise 的状态:

let p = Promise.resolve(42);

p.then(v => {
  console.log(v);
  return v + 1; // 相当于 Promise.resolve(v+1);
})
  .then(v => {
    console.log(v);
  })

// 42
// 43

返回一个 thenable 对象:

let p = Promise.resolve(42);
let thenable = {
  then(resolve, reject) {
    reject(44);
  }
};

p.then(v => {
  console.log(v);
  return thenable; // 相当于 Promise.resolve(thenable);
})
  .then(v => {
    console.log(v);
  })
  .catch(v => {
    console.log('error: ' + v);
  });

// 42
// error: 44

响应多个Promise

Promise.all() 方法接收一个参数并返回一个 Promise,该参数是含有多个Promise 对象的可迭代元素,例如数组。当每个 Promise对象的状态都为fulfilled 时,返回的 Promise 状态才是 fulfilled:

let p1 = Promise.resolve(1);
let p2 = new Promise((resolve, reject) => {
  resolve(2);
});
let p3 = new Promise((resolve, reject) => {
  resolve(3);
});

let p = Promise.all([p1, p2, p3]);
p.then(v => console.log(v));  // [ 1, 2, 3 ]

只要其中有一个Promise状态为 rejected,最后的返回 Promise的状态就为 rejected

let p1 = Promise.resolve(1);
let p2 = new Promise((resolve, reject) => {
  reject(new Error('boom'));
});
let p3 = new Promise((resolve, reject) => {
  resolve(3);
});

let p = Promise.all([p1, p2, p3]);
p.then(v => console.log(v)).catch(e => console.log(e.message)); // 'boom'

Promise.race()方法接收的参数和 all()一样,不同的是,传给 race()方法的 Promise 对象只要有一个状态发生改变,返回的 Promise 的状态就会改变,无需等其他的 Promise 状态都改变:

let p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(1);
  }, 0);
});

let p2 = Promise.resolve(2);
let p3 = new Promise((resolve, reject) => {
  reject(3);
});

let p = Promise.race([p1, p2, p3]);
p.then(v => console.log(v)).catch(e => console.log(e.message)); // 2

异步处理应用

在之前我们有一个模拟的异步任务:

function fetchData(url, cb) {
  setTimeout(() => {
    cb({ code: 0, data: url });
  }, 1000);
}

fetchData('aa.com', res => console.log(res.data));  // aa.com

我们可以利用 Promise 的方式改写这个异步任务:

function fetchData(url) {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve({ code: 0, data: url });
    }, 1000);
  });
}


fetchData('aa.com').then(res => console.log(res.data)); // aa.com

对于自动执行函数run()我们可以改写成支持 Promise 异步的形式:

function run(gen) {
  let g = gen();

  function next(data) {
    let result = g.next(data);

    if (result.done) return;

    let p = Promise.resolve(result.value);
    p.then(value => {
      next(value);
    }).catch(err => {
      g.throw(err);
    });
  }

  next();
}

参考

阮一峰es-promise

ES6 系列之我们来聊聊 Promise

@wozien wozien added the es6 label Oct 19, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant