是JavaScriptPromise异步的

Are JavaScript Promise asynchronous?

本文关键字:异步 JavaScriptPromise      更新时间:2024-05-21

只是一个简单的澄清问题:JavaScript Promise是异步的吗?我读了很多关于Promise和异步编程(即ajax请求)的文章。如果Promise不是异步的,我们如何使它成为异步的呢?

例如,我有一个函数将函数f和参数数组args包装在Promise中。f本质上没有任何东西是异步的。

function getPromise(f, args) {
 return new Promise(function(resolve, reject) {
  var result = f.apply(undefined, args);
  resolve(result);
 });
}

为了实现这种异步,我阅读了一些SO的文章,并决定setTimeout是许多人建议的使代码不阻塞的方法。

function getPromise(f, args) {
 return new Promise(function(resolve, reject) {
  setTimeout(function() { 
   var r = f.apply(undefined, args);
   resolve(r);
  }, 0);
 });
}

setTimeout的这种方法能使代码在Promise中不阻塞吗?

(请注意,我不依赖任何第三方Promise API,只依赖浏览器支持的内容)。

我认为您的工作存在误解。JavaScript代码是总是*阻塞;这是因为它在单个线程上运行。Javascript中异步编码风格的优点在于,像I/O这样的外部操作不需要阻塞该线程。但是,处理I/O响应的回调仍处于阻塞状态,没有其他JavaScript可以同时运行。

*除非您考虑运行多个进程(或在浏览器上下文中运行WebWorkers)。

现在针对您的具体问题:

只是一个快速澄清的问题:JavaScriptPromise是异步的吗?

不,传递到Promise构造函数的回调是立即同步执行的,尽管肯定可以启动异步任务,例如超时或写入文件,然后等待异步任务完成后再解析Promise;事实上,这是promise的主要用例。

这种带有setTimeout的方法能使代码在Promise中不阻塞吗?

不,它所做的只是改变执行顺序。脚本的其余部分将一直执行到完成,然后当它没有其他事情可做时,将执行setTimeout的回调。

澄清:

    console.log( 'a' );
    
    new Promise( function ( ) {
        console.log( 'b' );
        setTimeout( function ( ) {
            console.log( 'D' );
        }, 0 );
    } );
    // Other synchronous stuff, that possibly takes a very long time to process
    
    console.log( 'c' );

上述程序决定性地打印:

a
b
c
D

这是因为setTimeout的回调直到主线程没有任何事情可做(在记录"c"之后)才会执行。

我知道这是一个7年前的问题,但:

答案

  • Promise块是同步的
  • Promise回调块(resolve、reject)是同步的
  • 如果Promise块使用setTimeout块,则回调call(而不是块本身)可以是异步的

因此,为了回答Jane Wayne正在做的直接问题,是的,使用setTimeout块将使Promise块不阻塞。

解释

Javascript是同步的,它只有一个线程。Javascript引擎使用堆栈(Call Stack)来执行所有内容(LIFO)。

Web API是浏览器的一部分。例如,在那里可以找到setTimeout作为Window对象的一部分。

两个世界之间的通信是通过使用Event Loop管理的两个队列(Microtask和Macrotask队列,即FIFO)来完成的,这是一个不断运行的进程。

事件循环、微任务和宏任务队列

执行情况如下:

  • 使用调用堆栈执行所有脚本
  • Event Loop一直检查Call Stack是否为空
  • 如果Event Loop发现Call Stack为空,则开始从Microtask queueCall Stack传递回调(如果有的话)
  • 一旦Microtask queue为空,则Event LoopMacrotask queue执行相同操作

因此,进入Promise世界。

  • Promise块同步执行
  • Promise块中,可以有一些在Javascript引擎之外执行的东西(例如setTimeout块),这就是我们所说的异步,因为它在Javascript线程之外运行
  • Promise的回调(resolvereject)的执行可以认为是异步的,但块本身是同步执行的

因此,如果在Promise块中没有任何异步块,Promise将阻止您的执行。而且,如果在回调部分没有任何异步块,Promise将阻止您的执行。

示例:

console.log(`Start of file`)
const fourSeconds = 4000
// This will not block the execution
// This will go to the Macrotask queue
// Microtask queue has preference over it, so all the Promises not using setTimeout
setTimeout(() => {
    console.log(`Callback 1`)
})
// This will block the execution because the Promise block is synchronous
// But the result will be executed after all the Call Stack
// This will be the first Microtask printed
new Promise((resolve, reject) => {
    let start = Date.now()
    while (Date.now() - start <= fourSeconds) { }
    resolve(`Promise block resolved`)
}).then(result => console.log(result))
// This will not block the execution
// This will go into the Macrotask queue because of setTimeout
// Will block all the Macrotasks queued after it
// In this case will block the Macrotask "Callback 2" printing
new Promise((resolve, reject) => {
    setTimeout(() => resolve(`Promise callback block resolved`))
}).then(result => {
    let start = Date.now()
    while (Date.now() - start <= fourSeconds) { }
    console.log(result)
})
// This will not block the execution
// This will go to the Macrotask queue
// Microtask queue has preference over it
// Also the previous Macrotasks has preference over it, so this will be the last thing to be executed
setTimeout(() => {
    console.log(`Callback 2`)
})
// This will not block the execution
// It will go to the Microtask queue having preference over the setTimeout
// Also the previous Microtasks has preference over it
Promise.resolve(`Simply resolved`).then(result => console.log(result))
console.log(`End of file`)
/*
Output:
 
Start of file
[... execution blocked 4 seconds ...]
End of file
Promise block resolved
Simply resolved
Callback 1
[... execution blocked 4 seconds ...]
Promise callback block resolved
Callback 2
*/

const p = new Promise((resolve, reject) => {
  if (1 + 1 === 2) {
    resolve("A");
  } else {
    reject("B");
  }
});
p.then((name) => console.log(name)).catch((name) => console.log(name));
console.log("hello world");

Promise在处于挂起状态时不会阻塞下一行。因此,它是异步工作的。

您的MDN参考非常有用。Thx。如果你运行这个,你应该看到异步输出。

================================================================异步使用";承诺"

const log = console.log;
//---------------------------------------
class PromiseLab {
    play_promise_chain(start) {
        //"then" returns a promise, so we can chain
        const promise = new Promise((resolve, reject) => {      
            resolve(start);
        });     
        promise.then((start) => {
            log(`Value: "${start}" -- adding one`);
            return start + 1;
        }).then((start) => {
            log(`Value: "${start}" -- adding two`);
            return start + 2;
        }).then((start) => {
            log(`Value: "${start}" -- adding three`);
            return start + 3;
        }).catch((error) => {
            if (error) log(error);
        });             
    }
        
}
//---------------------------------------
const lab = new PromiseLab();
lab.play_promise_chain(100);
lab.play_promise_chain(200);

输出应该是异步的,比如:

Value: "100" -- adding one
Value: "200" -- adding one
Value: "101" -- adding two
Value: "201" -- adding two
Value: "103" -- adding three
Value: "203" -- adding three

================================================================同步使用";MyPromise";(例如基本js代码)

const log = console.log;
//---------------------------------------
class MyPromise {
    
    value(value) { this.value = value; }
    
    error(err) { this.error = err; }
    
    constructor(twoArgFct) {
        twoArgFct(
            aValue => this.value(aValue),
            anError => this.error(anError));    
    }
    
    then(resultHandler) { 
        const result = resultHandler(this.value);
        return new MyPromise((resolve, reject) => {     
            resolve(result);
        });
    }
    
    catch(errorHandler) { 
        errorHandler(this.error());
    }
    
}
//--------------------------------------
    
class MyPromiseLab {
    play_promise_chain(start) {
        //"then" returns a promise, so we can chain
        const promise = new MyPromise((resolve, reject) => {        
            resolve(start);
        });     
        promise.then((start) => {
            log(`Value: "${start}" -- adding one`);
            return start + 1;
        }).then((start) => {
            log(`Value: "${start}" -- adding two`);
            return start + 2;
        }).then((start) => {
            log(`Value: "${start}" -- adding three`);
            return start + 3;
        }).catch((error) => {
            if (error) log(error);
        });             
    }
        
}
//---------------------------------------
const lab = new MyPromiseLab();
lab.play_promise_chain(100);
lab.play_promise_chain(200);

输出应同步:

Value: "100" -- adding one
Value: "101" -- adding two
Value: "103" -- adding three
Value: "200" -- adding one
Value: "201" -- adding two
Value: "203" -- adding three